Skip to content

Astro — error handling

The loader throws exactly one error type, GhostwritrError, and it fails closed: a broken config or an unreachable feed stops the build rather than shipping an empty or partial collection. There are two distinct moments where it can throw.

Config errors throw synchronously, the moment ghostwritr() is called in src/content.config.ts. A missing or blank siteId, or an invalid staticBaseUrl, throws right there with code CONFIG and status 0 — the build never starts.

Feed errors throw during the build, when the loader’s load runs. A missing feed, a network failure, or a malformed payload throws while astro build is syncing the collection. The build fails.

src/content.config.ts
import { defineCollection } from "astro:content";
import { ghostwritr } from "@ghostwritr/astro";
// Throws synchronously if GHOSTWRITR_SITE_ID is unset → build never starts.
export const collections = {
articles: defineCollection({
loader: ghostwritr({ siteId: import.meta.env.GHOSTWRITR_SITE_ID }),
}),
};

GhostwritrError carries a status (the HTTP status, or 0 for config/network errors) and a code:

import { GhostwritrError } from "@ghostwritr/astro";
try {
// ...whatever wrapped the loader/feed call
} catch (err) {
if (err instanceof GhostwritrError) {
err.code; // see the table below
err.status; // HTTP status, or 0 for config/network
err.details; // extra context (e.g. the duplicate slug, the malformed body)
}
}

instanceof GhostwritrError works identically across @ghostwritr/astro, @ghostwritr/next, and @ghostwritr/feed — they all re-export the same class.

codestatusmeaning
CONFIG0bad options — missing siteId, invalid staticBaseUrl
NETWORK_ERROR0the request never completed (DNS, offline, fetch threw)
NOT_FOUND404no feed for this siteId — fails closed
RATE_LIMITED429feed origin rate-limited the build
SERVER_ERROR5xxfeed origin error
INVALID_RESPONSE0malformed feed, a non-JSON body, or a duplicate slug

The exported GhostwritrErrorCode type is open (a union plus string), so handle unknown codes gracefully.

You usually want the build to fail loudly — that’s the safe default and it needs no code. Catch GhostwritrError only when you have a deliberate fallback, for example a CI step that distinguishes a transient NETWORK_ERROR (retry the build) from a CONFIG error (fix the env and never retry). Don’t swallow NOT_FOUND into an empty collection; an empty blog deployed silently is worse than a failed build.

See Error handling for the cross-SDK error model.