withastro/astro · error · AstroError

PrerenderDynamicEndpointPathCollide

PrerenderDynamicEndpointPathCollide

Error message

Could not render `${pathname}` with an `undefined` param as the generated path will collide during prerendering. Prevent passing `undefined` as `params` for the endpoint's `getStaticPaths()` function, or add an additional extension to the endpoint's filename.

What it means

When an endpoint route's last path segment is entirely dynamic (e.g. `src/pages/api/[slug].js`), the generated output filename comes solely from the param value. If `getStaticPaths()` yields `undefined` for that last param, multiple endpoints would collapse onto the same output path, so `validatePrerenderEndpointCollision` throws `PrerenderDynamicEndpointPathCollide` during rendering instead of silently overwriting files.

Solutions

  1. Add an additional extension to the filename so the last segment is no longer solely dynamic, e.g. `src/pages/api/[slug].json.js` (params like `og-image/[...slug].png.ts` also work)
  2. Ensure `getStaticPaths()` always provides a concrete string for the last param — filter or default undefined values away
  3. If some entries legitimately have no value, generate them under a fixed-name route instead of a dynamic one

Example fix

# before
src/pages/api/[slug].js   # getStaticPaths returns { params: { slug: undefined } }

# after
src/pages/api/[slug].json.js   # last segment no longer solely dynamic
# and/or return concrete values: [{ params: { slug: 'item-1' } }]
Defensive patterns

Strategy: validation

Validate before calling

// Filter undefined last params out of getStaticPaths before returning
export async function getStaticPaths() {
  const items = await fetchItems();
  return items
    .filter((i) => i.slug !== undefined)
    .map((i) => ({ params: { slug: i.slug } }));
}

Prevention

When it happens

Trigger: `src/pages/api/[slug].js` with `getStaticPaths()` returning `[{ params: { slug: undefined } }]` or omitting the slug key entirely; a rest endpoint `[...path].js` whose last param resolves to undefined; generating extension-less endpoint outputs where the param is the only filename content.

Common situations: Building JSON APIs with a single dynamic segment and returning params built from optional data (value undefined when missing); migrating `[slug].json.ts` to `[slug].ts` and hitting the collision rule; params derived from CMS fields that can be null.

Related errors


AI-assisted analysis of withastro/astro@52e6c34790 (2026-08-18). Data as JSON: /api/errors/f2f89323c2e83027. Report an issue: GitHub.

Appendix: source

Thrown at packages/astro/src/core/render/params-and-props.ts:145

/**
 * If we have an endpoint at `src/pages/api/[slug].ts` that's prerendered, and the `slug`
 * is `undefined`, throw an error as we can't generate the `/api` file and `/api` directory
 * at the same time. Using something like `[slug].json.ts` instead will work.
 */
function validatePrerenderEndpointCollision(
	route: RouteData,
	mod: ComponentInstance,
	params: Params,
) {
	if (route.type === 'endpoint' && mod.getStaticPaths) {
		const lastSegment = route.segments[route.segments.length - 1];
		const paramValues = Object.values(params);
		const lastParam = paramValues[paramValues.length - 1];
		// Check last segment is solely `[slug]` or `[...slug]` case (dynamic). Make sure it's not
		// `foo[slug].js` by checking segment length === 1. Also check here if that param is undefined.
		if (lastSegment.length === 1 && lastSegment[0].dynamic && lastParam === undefined) {
			throw new AstroError({
				...AstroErrorData.PrerenderDynamicEndpointPathCollide,
				message: AstroErrorData.PrerenderDynamicEndpointPathCollide.message(route.route),
				hint: AstroErrorData.PrerenderDynamicEndpointPathCollide.hint(route.component),
				location: {
					file: route.component,
				},
			});
		}
	}
}

View on GitHub (pinned to 52e6c34790)