Fix: Invalid Type Returned by Astro Page

Error message:
Invalid type returned by Astro page.
Build & Compilation 2025-01-25

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 Response objects
  • Use Response.json() for JSON responses
  • Use Astro.redirect() for redirects in pages
  • Set appropriate status codes
  • Include Content-Type headers