Files
CompanySite/.agents/backend-specialist/BLOG_500_DEBUG.md
T
Clintchiz 0614ae6f85
E2E Test Suite / Critical User Journeys (push) Has been skipped
E2E Test Suite / API Integration Tests (push) Has been skipped
Deploy to Production / Build & Verify (push) Failing after 13s
Ping Search Engines / Notify Search Engines (push) Successful in 3s
Deploy to Production / Pre-Deploy Tests (push) Has been skipped
Deploy to Production / Deploy to Railway (push) Has been skipped
Deploy to Production / Deploy to Render (push) Has been skipped
Deploy to Production / Deploy to VPS (PM2) (push) Has been skipped
Deploy to Production / Deploy to Fly.io (push) Has been skipped
Deploy to Production / Post-Deploy Verification (push) Has been skipped
Deploy to Production / Notify on Failure (push) Successful in 1s
E2E Test Suite / Smoke Tests (P0) (push) Failing after 9m36s
E2E Test Suite / Form Interaction Tests (push) Failing after 12m6s
E2E Test Suite / Destructive & Chaos Tests (push) Failing after 11m46s
E2E Test Suite / Cross-Browser Regression (chromium) (push) Failing after 9m31s
E2E Test Suite / Cross-Browser Regression (firefox) (push) Failing after 11m5s
E2E Test Suite / Cross-Browser Regression (webkit) (push) Failing after 15m24s
E2E Test Suite / Security Header Tests (push) Failing after 7m55s
E2E Test Suite / Test Report Summary (push) Failing after 6s
E2E Test Suite / Mobile Device Tests (push) Failing after 3h12m28s
Uptime Monitor / Health & Response Time (push) Successful in 5s
Uptime Monitor / SSL Certificate (push) Successful in 3s
Uptime Monitor / Send Alerts (push) Has been skipped
Uptime Monitor / Record Uptime Success (push) Successful in 2s
Latest Updated Pages
2026-03-22 14:37:17 +05:30

4.1 KiB

Blog 500 Error — Debug Report

Agent: backend-specialist Date: 2026-03-21 Priority: High Status: RESOLVED


Summary

Individual blog post pages (/blog/<slug>) returned a 500 Internal Server Error because src/pages/blog/[...slug].astro used getStaticPaths() — a Static Site Generation (SSG) API that is not valid in SSR (output: 'server') mode.


Root Cause

The Conflict

File Setting
astro.config.mjs output: 'server' (full SSR, Node adapter)
src/pages/blog/[...slug].astro Used getStaticPaths() (SSG-only API)

Astro's getStaticPaths() is only valid in output: 'static' mode. When the server receives a request for /blog/getting-started-with-astro, Astro attempts to call getStaticPaths() at request time, which is unsupported and causes a runtime error propagated as a 500.

Error Flow

GET /blog/getting-started-with-astro
  → Astro SSR handler invokes [...slug].astro
  → getStaticPaths() is called at request time
  → Astro throws: "getStaticPaths() is not available in server mode"
  → middleware.ts catches and re-throws (line 73)
  → 500 Internal Server Error returned to client

Files Examined

File Finding
astro.config.mjs output: 'server' with @astrojs/node standalone adapter
src/pages/blog/[...slug].astro Used getStaticPaths() — incompatible with SSR
src/pages/blog/index.astro Correctly uses getCollection() at the top level (valid in SSR)
src/content/config.ts Schema is valid; blog collection properly defined
src/content/blog/*.md All 3 posts have valid frontmatter matching the schema
src/middleware.ts Re-throws errors from next() — confirms 500 path

Fix Applied

File: src/pages/blog/[...slug].astro

Before (broken SSG pattern)

import { getCollection, type CollectionEntry } from 'astro:content';

export async function getStaticPaths() {
  const posts = await getCollection('blog', ({ data }) => !data.draft);
  return posts.map((post) => ({
    params: { slug: post.slug },
    props: { post },
  }));
}

type Props = { post: CollectionEntry<'blog'> };
const { post } = Astro.props;
const { Content } = await post.render();

After (correct SSR pattern)

import { getEntry } from 'astro:content';

const { slug } = Astro.params;

if (!slug) {
  return Astro.redirect('/blog');
}

const post = await getEntry('blog', slug);

if (!post || post.data.draft) {
  return Astro.redirect('/404');
}

const { Content } = await post.render();

Key Changes

  • Removed getStaticPaths() entirely
  • Replaced getCollection import with getEntry
  • Reads slug dynamically from Astro.params at request time
  • Handles missing/draft posts with a redirect to /404
  • No Props type annotation needed (props come from params now)

Why getEntry() is the Correct SSR Approach

In SSR mode, Astro dynamically serves each request. The URL slug is available at runtime via Astro.params. getEntry('blog', slug) fetches a single content collection entry by its slug — this is the official Astro SSR pattern for content collections.


Secondary Observations

These are not the cause of the 500 but are worth noting:

  1. index.astro works fine — it uses getCollection() directly in the frontmatter (not in getStaticPaths()), which is valid in SSR since it runs per-request.

  2. Content is valid — all 3 markdown posts pass the Zod schema in config.ts. No frontmatter issues.

  3. Middleware correctly re-throws — the error handling in middleware.ts was functioning as designed; the 500 originated from the page handler, not the middleware itself.

  4. Blog index not affected/blog (list page) was working correctly; only individual post routes were broken.


Testing the Fix

After deploying, verify the following URLs return 200:

GET /blog/getting-started-with-astro   → 200 OK
GET /blog/ai-transforming-business     → 200 OK
GET /blog/cloud-migration-guide        → 200 OK
GET /blog/nonexistent-slug             → redirect to /404