Problem
Running next build fails during the prerender phase when the sitemap route queries a database that is not reachable at build time:
Error occurred prerendering page "/sitemap.xml". Read more: https://nextjs.org/docs/messages/prerender-error
Error: No database connection string was provided to `neon()`. You can either set
the POSTGRES_URL environment variable or pass a connection string to `neon()`.
Export encountered errors on following paths:
/sitemap.xml/[[...__metadata_id__]]/route
The sitemap route that triggers this error:
// app/sitemap.ts
import { db } from "@/lib/db";
import { posts } from "@/lib/schema";
export default async function sitemap() {
const allPosts = await db.select().from(posts);
return allPosts.map((post) => ({
url: `https://example.com/posts/${post.slug}`,
lastModified: post.updatedAt,
}));
}
Solution
Option 1: Mark the route as dynamic (recommended)
Add the dynamic route segment config to force the sitemap to render at request time instead of build time:
// app/sitemap.ts
import { db } from "@/lib/db";
import { posts } from "@/lib/schema";
export const dynamic = "force-dynamic";
export default async function sitemap() {
const allPosts = await db.select().from(posts);
return allPosts.map((post) => ({
url: `https://example.com/posts/${post.slug}`,
lastModified: post.updatedAt,
}));
}
Option 2: Wrap the database call in a try-catch with a fallback
// app/sitemap.ts
import { db } from "@/lib/db";
import { posts } from "@/lib/schema";
export default async function sitemap() {
try {
const allPosts = await db.select().from(posts);
return allPosts.map((post) => ({
url: `https://example.com/posts/${post.slug}`,
lastModified: post.updatedAt,
}));
} catch {
return [];
}
}
Why It Works
By default, Next.js App Router treats routes without dynamic APIs (cookies(), headers(), searchParams) as static and prerenders them at build time. If the route queries a database whose connection string is not available in the build environment, the prerender fails.
Setting export const dynamic = 'force-dynamic' tells Next.js to skip prerendering for that route. The sitemap is generated on each request at runtime when the database connection is available. This adds minimal overhead since sitemaps are typically cached by CDNs.
Context
- Next.js 14+ App Router with the built-in
sitemap.tsconvention - Applies to any database ORM or client: Drizzle, Prisma, Neon serverless driver, Supabase
- Also affects other dynamic routes that query external services at build time (RSS feeds,
robots.ts,og:imageroutes) - The try-catch approach (Option 2) still prerenders with an empty result, which may cause SEO gaps until the first runtime request