withastro/astro · error · AstroError
OnlyResponseCanBeReturned
OnlyResponseCanBeReturned
Error message
Route `${route}` returned a `${returnedValue}`. Only a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) can be returned from Astro files. What it means
renderPage validates the value produced by a page component: allowed outcomes are a Response (redirect/404), a HeadAndContent wrapper whose content is a valid RenderTemplateResult, or a RenderTemplateResult itself (what the compiled .astro body normally produces). This site fires when the factory result is HeadAndContent but its content is not a render template, i.e. the page returned structured data where markup was required.
Solutions
- Return a Response for control flow: return new Response(null, { status: 404 }) or Astro.redirect(...)
- Otherwise return nothing and let the page template render
- Move pure data endpoints to a .js/.ts API route file instead of an .astro page
Example fix
// before
---
const post = await getPost(Astro.params.slug);
if (!post) return null;
---
// after
---
const post = await getPost(Astro.params.slug);
if (!post) return new Response(null, { status: 404 });
--- Defensive patterns
Strategy: type-guard
Validate before calling
// in wrappers that return page results, assert the shape
const result = await loadPageResult();
if (!(result instanceof Response)) {
throw new Error('Page must return a Response or render its template');
} Type guard
function isResponse(value: unknown): value is Response {
return value instanceof Response;
} Prevention
- In .astro frontmatter, either return a Response (Astro.redirect, 404) or return nothing
- Never 'return;' bare or return data objects from pages
- Serve data-only routes from .js/.ts endpoint files, not .astro pages
When it happens
Trigger: An .astro frontmatter returning a non-Response, non-template value that gets wrapped as head+content: return someObject, return null after a guard clause, or a layout/content combination whose content is data instead of template output.
Common situations: Treating .astro frontmatter like an endpoint (return data); early-return pattern 'if (!post) return;' with no value; returning the result of a helper that yields plain objects; porting pages from frameworks where returning strings/objects from pages is normal.
Related errors
- EndpointDidNotReturnAResponse
- ResponseSentError
- GetImageNotUsedOnServer
- MiddlewareNotAResponse
- NoAdapterInstalledServerIslands
AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18).
Data as JSON: /api/errors/ab2c2d27036d56d8.
Report an issue: GitHub.
Appendix: source
Thrown at packages/astro/src/runtime/server/render/astro/render.ts:323
}
async function callComponentAsTemplateResultOrResponse(
result: SSRResult,
componentFactory: AstroComponentFactory,
props: any,
children: any,
route?: RouteData,
) {
const factoryResult = await componentFactory(result, props, children);
if (factoryResult instanceof Response) {
return factoryResult;
}
// we check if the component we attempt to render is a head+content
else if (isHeadAndContent(factoryResult)) {
// we make sure that content is valid template result
if (!isRenderTemplateResult(factoryResult.content)) {
throw new AstroError({
...AstroErrorData.OnlyResponseCanBeReturned,
message: AstroErrorData.OnlyResponseCanBeReturned.message(
route?.route,
typeof factoryResult,
),
location: {
file: route?.component,
},
});
}
// return the content
return factoryResult.content;
} else if (!isRenderTemplateResult(factoryResult)) {
throw new AstroError({
...AstroErrorData.OnlyResponseCanBeReturned,
message: AstroErrorData.OnlyResponseCanBeReturned.message(route?.route, typeof factoryResult),
location: {View on GitHub (pinned to 52e6c34790)