withastro/astro · error · AstroError
MiddlewareNotAResponse
MiddlewareNotAResponse
Error message
Any data returned from middleware must be a valid `Response` object.
What it means
callMiddleware enforces the middleware contract: a middleware that calls next() may return nothing (the response from next is used) or must return a Response instance. This branch throws AstroErrorData.MiddlewareNotAResponse when next() was called and the middleware returned a defined value that fails `value instanceof Response` — a plain object, string, or a Response created in another runtime/Realm (e.g. a node-fetch or cross-realm Response).
Solutions
- Return the Response you got: `const res = await next(); return res;`
- Build a real Response when transforming: return new Response(body, { status, headers })
- Remove fetch polyfills from server code so instanceof checks use the global Response
Example fix
// before
export const onRequest = async (context, next) => {
const response = await next();
if (!response.ok) return { status: 500 };
return response;
};
// after
export const onRequest = async (context, next) => {
const response = await next();
if (!response.ok) return new Response('error', { status: 500 });
return response;
}; Defensive patterns
Strategy: validation
Validate before calling
const result = await next();
if (!(result instanceof Response)) {
throw new TypeError('Middleware returned a non-Response value after next()');
}
return result; Type guard
const isResponse = (value: unknown): value is Response => value instanceof Response;
Prevention
- Default to `return await next()` in every middleware that does not short-circuit
- When transforming the response, always rebuild with new Response(body, { status, headers })
- Avoid fetch polyfills in server code so instanceof Response checks are consistent
When it happens
Trigger: A middleware that does `const res = await next(); return { ...res }` or returns a status object like { status: 500 }; returning a Response produced by a fetch polyfill or another Realm so the instanceof check against the global Response fails.
Common situations: Copying Express middleware patterns that return plain result objects; using node-fetch or an older undici alongside the global fetch in the same process; middleware returning response.json() or a mutated copy of headers instead of the Response itself.
Related errors
- EndpointDidNotReturnAResponse
- OnlyResponseCanBeReturned
- `Astro.session` was accessed but no session storage is…
- `context.next` is not implemented for serverless functions
- FetchState not found on APIContext. `next(payload)`…
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/1e884bb44ef7ddbc.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/core/middleware/callMiddleware.ts:76
};
const middlewarePromise = onRequest(apiContext, next);
return await Promise.resolve(middlewarePromise).then(async (value) => {
// first we check if `next` was called
if (nextCalled) {
/**
* Then we check if a value is returned. If so, we need to return the value returned by the
* middleware.
* e.g.
* ```js
* const response = await next();
* const new Response(null, { status: 500, headers: response.headers });
* ```
*/
if (typeof value !== 'undefined') {
if (value instanceof Response === false) {
throw new AstroError(AstroErrorData.MiddlewareNotAResponse);
}
return value;
} else {
/**
* Here we handle the case where `next` was called and returned nothing.
*/
if (responseFunctionPromise) {
return responseFunctionPromise;
} else {
throw new AstroError(AstroErrorData.MiddlewareNotAResponse);
}
}
} else if (typeof value === 'undefined') {
/**
* There might be cases where `next` isn't called and the middleware **must** return
* something.
*
* If not thing is returned, then we raise an Astro error.View on GitHub (pinned to 52e6c34790)