withastro/astro · error · Error

FetchState not found on APIContext. This is an internal erro

Error message

FetchState not found on APIContext. This is an internal error — the context was not created through Astro's request pipeline.

What it means

Astro's request pipeline stashes a per-request FetchState on the APIContext under a module-private symbol (fetchStateSymbol). getFetchStateFromAPIContext() reads that symbol and throws this error when it is absent, meaning the context object was never created through Astro's request pipeline (FetchState.getAPIContext / app.render). It is an internal invariant violation, not a configuration problem: either someone hand-built an APIContext, or two copies of Astro core are running and the symbol from one copy is not found by the other.

Source

Thrown at packages/astro/src/core/fetch/fetch-state.ts:139

	status: number;

	/**
	 * Triggers a rewrite to a different route.
	 *
	 * [Astro reference](https://docs.astro.build/en/guides/routing/#rewrites)
	 */
	rewrite(payload: RewritePayload): Promise<Response>;
}

/**
 * Retrieves the `FetchState` stashed on an `APIContext` by
 * `FetchState.getAPIContext()`. Throws if not found — this indicates
 * the context was not created through Astro's request pipeline.
 */
export function getFetchStateFromAPIContext(context: APIContext): FetchState {
	const state = (context as any)[fetchStateSymbol] as FetchState | undefined;
	if (!state) {
		throw new Error(
			"FetchState not found on APIContext. This is an internal error — the context was not created through Astro's request pipeline.",
		);
	}
	return state;
}

/**
 * Holds per-request state as it flows through the handler pipeline.
 *
 * **This class is user-facing** via `astro/fetch` and `astro/hono`.
 * The {@link AstroFetchState} interface defines the stable public
 * surface. Members not on that interface are internal and
 * may change without notice.
 *
 * Performance note: fields on this class are plain properties — ES
 * private fields (`#foo`) have a non-zero per-access cost in V8
 * which is measurable on the hot render path, so `#` is used only
 * for rarely-accessed memoized caches and Maps.

View on GitHub (pinned to e294953aa8)

Solutions

  1. Route requests through Astro's own pipeline (app.render / the built server entry) instead of passing a hand-made context into Astro internals
  2. If you write an adapter, delegate rendering to Astro's handler so contexts are stamped by FetchState, never construct APIContext yourself
  3. In tests, use the built server entry or Astro's official test utilities rather than fabricating contexts
  4. Check for duplicate astro copies (pnpm why astro / npm ls astro) and dedupe with version overrides so the symbol module is loaded once
  5. If every party involved is first-party Astro code, report it as an Astro bug with a reproduction

Example fix

// before (custom adapter)
const context = { request, params: {}, locals: {} } as APIContext;
await someAstroInternal(context);

// after — let Astro create the context via its pipeline
const response = await app.render(request);
Defensive patterns

Strategy: try-catch

Try / catch

try {
  // call the Astro internal that needs the pipeline context
} catch (err) {
  if (err instanceof Error && err.message.includes('FetchState not found on APIContext')) {
    throw new Error('Context was not created by the Astro request pipeline — pass requests through app.render()', { cause: err });
  }
  throw err;
}

Prevention

When it happens

Trigger: An internal helper that expects a pipeline-created context receives one built by hand or by a third party: a custom adapter calling Astro internals with its own fabricated context object, a unit test constructing an APIContext literal, or duplicate astro package instances in node_modules (e.g. pnpm resolving two versions) so fetchStateSymbol differs between the stamper and the reader.

Common situations: Writing a custom adapter or integration that builds its own context instead of delegating to Astro's handler; unit tests that fake an APIContext; monorepos where several @astrojs/* integrations pin different astro versions, loading astro core twice; mixing a locally linked astro with a registry-installed one.

Related errors


AI-assisted analysis of withastro/astro@e294953aa8 (2026-08-18). Data as JSON: /api/errors/f921ca793bb24883. Report an issue: GitHub.