Fix: Prerendered Route Generates Same Path as Another Route in Astro

Error message:
Prerendered route generates the same path as another route.
Routing & Pages 2025-01-25

What Causes This Error?

This error occurs when two different routes in your Astro project generate the same output path. Each page must have a unique URL path.

The Problem

src/pages/
├── blog.astro          # Generates /blog
└── blog/
    └── index.astro     # Also generates /blog ❌ Conflict!
---
// src/pages/[slug].astro
export function getStaticPaths() {
  return [
    { params: { slug: 'about' } },  // Generates /about
  ];
}
---

// src/pages/about.astro also exists! ❌ Conflict!

The Fix

Remove Duplicate Routes

src/pages/
├── blog/
│   └── index.astro     # ✅ Only one generates /blog

Rename Conflicting Files

src/pages/
├── blog-page.astro     # Generates /blog-page
└── blog/
    └── index.astro     # Generates /blog ✅ No conflict

Exclude From Dynamic Route

---
// src/pages/[slug].astro
export function getStaticPaths() {
  const pages = await getPages();

  // ✅ Filter out slugs that conflict with static pages
  const staticPages = ['about', 'contact', 'blog'];

  return pages
    .filter((page) => !staticPages.includes(page.slug))
    .map((page) => ({
      params: { slug: page.slug },
    }));
}
---

Common Scenarios

Index Page Conflicts

# ❌ Both generate /products
src/pages/products.astro
src/pages/products/index.astro

# ✅ Choose one
src/pages/products/index.astro  # Use this for /products

Dynamic and Static Overlap

---
// src/pages/posts/[slug].astro

// If you also have src/pages/posts/featured.astro
export function getStaticPaths() {
  const posts = await getPosts();

  // ❌ If a post has slug 'featured', it conflicts
  return posts.map((post) => ({
    params: { slug: post.slug },
  }));
}

// ✅ Exclude reserved slugs
export function getStaticPaths() {
  const posts = await getPosts();
  const reserved = ['featured', 'archive', 'tags'];

  return posts
    .filter((post) => !reserved.includes(post.slug))
    .map((post) => ({
      params: { slug: post.slug },
    }));
}
---

Trailing Slash Issues

// astro.config.mjs
export default defineConfig({
  trailingSlash: 'always', // or 'never' or 'ignore'
});
# With trailingSlash: 'always'
/blog/ and /blog are different
But both generate the same file!

Rest Parameters Catch-All

---
// src/pages/[...path].astro
export function getStaticPaths() {
  return [
    { params: { path: undefined } },  // Generates /
    { params: { path: 'about' } },    // Conflicts if /about.astro exists
  ];
}

// ✅ Be specific with catch-all routes
export function getStaticPaths() {
  return [
    { params: { path: 'docs' } },
    { params: { path: 'docs/intro' } },
    { params: { path: 'docs/api' } },
  ];
}
---

Localized Routes

---
// src/pages/[lang]/about.astro
export function getStaticPaths() {
  return [
    { params: { lang: 'en' } },   // /en/about
    { params: { lang: '' } },     // /about - might conflict!
  ];
}

// ✅ Use explicit language codes
export function getStaticPaths() {
  return [
    { params: { lang: 'en' } },
    { params: { lang: 'es' } },
  ];
}
---

Quick Checklist

  • Check for duplicate files generating same path
  • Use index.astro OR name.astro, not both
  • Filter reserved slugs from dynamic routes
  • Be careful with rest parameters [...path]
  • Check trailingSlash configuration