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

A prerendered endpoint whose last path segment is purely dynamic (e.g. [slug].ts) received an undefined param value. An undefined last param produces an empty trailing segment, colliding with the parent directory's index output. Astro aborts to avoid overwriting another generated file.

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 d081033d5f)

Solutions

  1. Stop passing undefined for the last-segment param — always provide a concrete string.
  2. Add an extension to the filename so the output path differs, e.g. rename [slug].ts to [slug].json.ts.
  3. Split the route: a dedicated index file for the empty case plus the dynamic file for named values.

Example fix

// before — file: src/pages/api/[slug].ts
export async function getStaticPaths() {
  return [{ params: { slug: undefined } }]; // collides
}

// after — rename file to src/pages/api/[slug].json.ts
export async function getStaticPaths() {
  return [{ params: { slug: 'item' } }];
}
Defensive patterns

Strategy: validation

Validate before calling

function lastParamDefined(params: Record<string, unknown>, key: string): boolean {
  return params[key] !== undefined;
}
// in getStaticPaths, filter or fix entries where the last-segment param is undefined

Type guard

function hasDefinedLastParam(params: Record<string, unknown>, lastKey: string): params is Record<string, string> {
  return typeof params[lastKey] === 'string';
}

Prevention

When it happens

Trigger: Endpoint file pages/api/[slug].ts with getStaticPaths returning { params: { slug: undefined } }; a rest param endpoint with an empty value; data source yielding undefined for the last param.

Common situations: Migrating a page-style route to an endpoint; conditionally returning undefined params; wanting an 'index' style output from a dynamic endpoint.

Related errors


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