withastro/astro · error · AstroError

MiddlewareNotAResponse

MiddlewareNotAResponse

Error message

Any data returned from middleware must be a valid `Response` object.

What it means

Thrown when middleware called next() and then returned a defined value that is not a Response instance. After awaiting next() the middleware may return next()'s Response or a new Response, but returning any other type (object, string, number) is rejected because the runtime must emit an HTTP Response.

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 d081033d5f)

Solutions

  1. Wrap returned data in a Response: `return new Response(JSON.stringify(data), { headers: { 'content-type': 'application/json' } })`.
  2. If you only meant to pass through, return the result of next(): `return await next()`.
  3. Return undefined after calling next() to let the downstream Response propagate.

Example fix

// before
export const onRequest = async (ctx, next) => {
  await next();
  return { ok: true };
};

// after
export const onRequest = async (ctx, next) => {
  await next();
  return Response.json({ ok: true });
};
Defensive patterns

Strategy: type-guard

Validate before calling

// Ensure middleware returns a Response or undefined (after next).
export const onRequest = async (ctx, next) => {
  await next();
  const payload = { ok: true };
  return Response.json(payload); // Response, not raw object
};

Type guard

function isResponse(v: unknown): v is Response {
  return v instanceof Response;
}

Try / catch

try {
  await callMiddleware(handler, ctx, render);
} catch (e) {
  if (e instanceof Error && /valid .Response./i.test(e.message)) {
    // middleware returned a non-Response; fix the return
  }
  throw e;
}

Prevention

When it happens

Trigger: A middleware does `await next()` and then `return someObject` or `return 'ok'` where the value is not a Response. The check `value instanceof Response === false` at line 76 of callMiddleware.ts fires for the nextCalled + defined value branch.

Common situations: Returning a JSON object or status string instead of `Response.json(...)`; returning locals or a payload by mistake; partial refactor where a helper that used to return a Response now returns data.

Related errors


AI-assisted analysis of withastro/astro@d081033d5f (2026-08-12). Data as JSON: /api/errors/1e884bb44ef7ddbc. Report an issue: GitHub.