What Causes This Error?
This error occurs when an Astro page or endpoint returns something other than a Response object. In SSR mode, pages and API routes must return valid Response objects.
The Problem
// src/pages/api/data.ts
export async function GET() {
// ❌ Returning plain object instead of Response
return { data: 'hello' };
}
---
// src/pages/page.astro
export const prerender = false;
// ❌ Returning non-Response from component script
return { redirect: '/other' };
---
The Fix
Return a Response Object
// src/pages/api/data.ts
export async function GET() {
// ✅ Return proper Response
return new Response(JSON.stringify({ data: 'hello' }), {
headers: { 'Content-Type': 'application/json' },
});
}
Use Astro Helpers
// src/pages/api/data.ts
export async function GET() {
// ✅ Using Response.json() shorthand
return Response.json({ data: 'hello' });
}
Common Scenarios
API Endpoints
// src/pages/api/users.ts
export const prerender = false;
export async function GET() {
const users = await fetchUsers();
return new Response(JSON.stringify(users), {
status: 200,
headers: {
'Content-Type': 'application/json',
},
});
}
export async function POST({ request }) {
const body = await request.json();
// Process data...
return new Response(JSON.stringify({ success: true }), {
status: 201,
headers: {
'Content-Type': 'application/json',
},
});
}
Redirects
// src/pages/api/redirect.ts
export async function GET() {
// ✅ Proper redirect Response
return new Response(null, {
status: 302,
headers: {
Location: '/new-location',
},
});
}
---
// src/pages/old-page.astro
export const prerender = false;
// ✅ Use Astro.redirect
return Astro.redirect('/new-page', 301);
---
Error Responses
// src/pages/api/protected.ts
export async function GET({ cookies }) {
const session = cookies.get('session');
if (!session) {
// ✅ Return error Response
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
return Response.json({ data: 'secret' });
}
Streaming Responses
// src/pages/api/stream.ts
export async function GET() {
const stream = new ReadableStream({
start(controller) {
controller.enqueue('Hello ');
controller.enqueue('World');
controller.close();
},
});
// ✅ Return streaming Response
return new Response(stream, {
headers: { 'Content-Type': 'text/plain' },
});
}
File Downloads
// src/pages/api/download.ts
export async function GET() {
const fileContent = await readFile('./data.csv');
return new Response(fileContent, {
headers: {
'Content-Type': 'text/csv',
'Content-Disposition': 'attachment; filename="data.csv"',
},
});
}
No Content Response
// src/pages/api/delete.ts
export async function DELETE({ params }) {
await deleteItem(params.id);
// ✅ 204 No Content
return new Response(null, { status: 204 });
}
Using Response Utilities
// Helpful Response patterns
export async function GET() {
// JSON response
return Response.json({ data: 'value' });
// Redirect
return Response.redirect('https://example.com', 302);
// Text
return new Response('Plain text');
// HTML
return new Response('<h1>Hello</h1>', {
headers: { 'Content-Type': 'text/html' },
});
}
Quick Checklist
- API routes must return
Responseobjects - Use
Response.json()for JSON responses - Use
Astro.redirect()for redirects in pages - Set appropriate status codes
- Include
Content-Typeheaders