remotion-dev/remotion · error · Error

Unexpected build output path

Error message

Unexpected build output path: ${output.path}

What it means

The promo-pages bundle script copies every esbuild build output into the dist/ directory, but first normalizes the output path and rejects any file that resolves outside dist/. If a relative output path still starts with '../' after stripping the 'dist/' prefix, the build fails fast rather than writing files outside the intended output directory.

Solutions

  1. Check the esbuild config in bundle.ts: set `outbase` (usually the source root) so all outputs land under dist/ without '../' segments.
  2. Move entry points so they all live below the directory used as outbase.
  3. Log output.path (console.log(output.path)) right before the throw to see which file escapes and fix its entry/asset config.
  4. If the file genuinely must live outside dist/, copy it explicitly after the build instead of routing it through esbuild outputs.

Example fix

// before
await esbuild.build({ entryPoints: ['./src/index.ts', '../shared/entry.ts'], outdir: 'dist' });
// after
await esbuild.build({ entryPoints: ['./src/index.ts', './src/shared-entry.ts'], outdir: 'dist', outbase: 'src' });
Defensive patterns

Strategy: validation

Validate before calling

// pre-validate the esbuild config mapping
const outbase = 'src';
for (const entry of entryPoints) {
  const rel = path.relative(outbase, entry);
  if (rel.startsWith('..')) throw new Error(`Entry outside outbase: ${entry}`);
}

Try / catch

try {
  await build();
} catch (e) {
  if (String(e.message).startsWith('Unexpected build output path')) {
    console.error('Fix esbuild outbase/outdir so outputs stay under dist/', e.message);
    process.exit(1);
  }
  throw e;
}

Prevention

When it happens

Trigger: Running the bundle script (bun packages/promo-pages/bundle.ts or its build pipeline) when esbuild emits an output file whose resolved path escapes dist/, e.g. a chunk or asset with an outbase/outdir misconfiguration producing paths like '../shared/foo.js'.

Common situations: Misconfigured esbuild `outbase` after moving entry points; adding a new entry point in a parent directory; changing `outdir` so emitted chunk paths contain '..'; symlinked or nested entry files changing relative path computation.

Understand the failure class

Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.

Related errors


AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18). Data as JSON: /api/errors/874fa9e30a4d55fb. Report an issue: GitHub.

Appendix: source

Thrown at packages/promo-pages/bundle.ts:94

for (const result of results) {
	if (!result.success) {
		console.log(result.logs.join('\n'));
		process.exit(1);
	}

	for (const output of result.outputs) {
		// On Windows, Bun may return absolute output paths here. Normalize them back
		// into the local dist directory so we don't accidentally write invalid paths.
		const relativeOutputPath = path.isAbsolute(output.path)
			? path.relative(outdir, output.path)
			: output.path;
		const normalizedOutputPath = relativeOutputPath.replaceAll('\\', '/');
		const outputPathWithoutDistPrefix = normalizedOutputPath.startsWith('dist/')
			? normalizedOutputPath.slice('dist/'.length)
			: normalizedOutputPath;

		if (outputPathWithoutDistPrefix.startsWith('../')) {
			throw new Error(`Unexpected build output path: ${output.path}`);
		}

		await Bun.write(
			path.join('dist', outputPathWithoutDistPrefix),
			await output.text(),
		);
	}
}

export {};

View on GitHub (pinned to 46a3a6bf13)