withastro/astro · error · AstroError
EndpointDidNotReturnAResponse
EndpointDidNotReturnAResponse
Error message
An endpoint must return either a `Response`, or a `Promise` that resolves with a `Response`.
What it means
An API route endpoint (a `.ts`/`.js` file under `src/pages/` exporting `GET`/`POST`/etc.) ran its handler, but the resolved value is not a `Response` instance (it was falsy, or `response instanceof Response` is false). Astro endpoints are contract-bound to return exactly a `Response`. This is the `EndpointDidNotReturnAResponse` AstroError.
Source
Thrown at packages/astro/src/runtime/server/endpoint.ts:64
: ''),
);
// No handler matching the verb found, so this should be a
// 404. Should be handled by 404.astro route if possible.
return new Response(null, { status: 404 });
}
if (typeof handler !== 'function') {
logger.error(
'router',
`The route "${
url.pathname
}" exports a value for the method "${method}", but it is of the type ${typeof handler} instead of a function.`,
);
return new Response(null, { status: 500 });
}
let response = await handler.call(mod, context);
if (!response || response instanceof Response === false) {
throw new AstroError(EndpointDidNotReturnAResponse);
}
// Endpoints explicitly returning 404 or 500 response status should
// NOT be subject to rerouting to 404.astro or 500.astro.
if (state && REROUTABLE_STATUS_CODES.includes(response.status)) {
state.skipErrorReroute = true;
}
if (method === 'HEAD') {
// make sure HEAD responses doesnt have body
return new Response(null, response);
}
return response;
}
View on GitHub (pinned to d081033d5f)
Solutions
- Ensure every code path in the endpoint handler returns a `Response` (check early returns and the final statement).
- Wrap data payloads: `return new Response(JSON.stringify(data), { headers: { 'content-type': 'application/json' } })` or `return Response.json(data)`.
- For empty/204-style replies use `return new Response(null, { status: 204 })`.
- Add a TypeScript return type of `Promise<Response>` so the compiler flags non-Response returns.
Example fix
// before
export const GET = async ({ url }) => {
const data = await db.query();
return data; // plain object -> throws
};
// after
export const GET = async ({ url }): Promise<Response> => {
const data = await db.query();
return Response.json(data);
}; Defensive patterns
Strategy: type-guard
Validate before calling
// Ensure every endpoint handler returns a Response
function assertResponse(value: unknown): Response {
if (!(value instanceof Response)) {
throw new Error('Endpoint handler must return a Response');
}
return value;
} Type guard
const isResponse = (v: unknown): v is Response => v instanceof Response;
// Usage in a handler:
export const GET = async (ctx): Promise<Response> => {
const res = await handler(ctx);
return isResponse(res) ? res : Response.json(res);
}; Try / catch
// Wrap external calls so a thrown/non-Response result never escapes
export const GET = async (ctx): Promise<Response> => {
try {
const data = await loadData(ctx);
return Response.json(data);
} catch (e) {
return Response.json({ error: String(e) }, { status: 500 });
}
}; Prevention
- Type every endpoint handler as `Promise<Response>` so the compiler rejects non-Response returns.
- Always wrap data with `Response.json(...)` or `new Response(...)`.
- Audit early-return branches to confirm each returns a Response.
When it happens
Trigger: The handler returns a plain object, array, string, number, `null`, or `undefined`; an async handler has a code path with no `return`; the handler returns a `Promise` that resolves to a non-Response; the handler returns a fetch result wrapper or custom class that isn't a `Response`.
Common situations: Returning JSON data directly (`return { ok: true }`) instead of wrapping it; forgetting `return` in an early-exit branch of an async handler; returning `Astro.redirect()`-like values from a helper that isn't a Response; migrating a page to an endpoint and forgetting the Response wrapping.
Related errors
- ActionsReturnedInvalidDataError
- ResponseSentError
- MiddlewareNotAResponse
- MiddlewareNoDataOrNextCalled
- RedirectWithNoLocation
AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12).
Data as JSON: /api/errors/452818059a0f309b.
Report an issue: GitHub.