remotion-dev/remotion · error · Error

${newRelativePath} already exists

Error message

${newRelativePath} already exists

What it means

renameStaticFile refuses to overwrite an existing public file: if the normalized new path is already a key in canonical publicFiles, it throws before mutating. This is an intentional non-destructive guard so renames cannot silently clobber another asset.

Source

Thrown at packages/browser-studio/src/browser-studio-project-controller.ts:536

			undoStack.length = 0;
			redoStack.length = 0;
			emit(getUndoRedoEvent());
		},
		renameStaticFile: ({oldRelativePath, newRelativePath}) => {
			try {
				const oldPath = normalizePublicFilePath(oldRelativePath);
				const newPath = normalizePublicFilePath(newRelativePath);
				if (oldPath === newPath) {
					return Promise.resolve({success: true});
				}

				const publicFiles = getCanonicalPublicFiles(getProject());
				if (publicFiles[oldPath] === undefined) {
					throw new Error(`${oldRelativePath} does not exist`);
				}

				if (publicFiles[newPath] !== undefined) {
					throw new Error(`${newRelativePath} already exists`);
				}

				applyMutation({
					fileName: oldPath,
					nodePathMutationFiles: null,
					mutate: (project) => {
						const nextPublicFiles = getCanonicalPublicFiles(project);
						nextPublicFiles[newPath] = nextPublicFiles[oldPath];
						delete nextPublicFiles[oldPath];
						return {...project, publicFiles: nextPublicFiles};
					},
				});
				return Promise.resolve({success: true});
			} catch (error) {
				return Promise.reject(error);
			}
		},
		subscribeToEvent: (listener) => {

View on GitHub (pinned to 78fe4bb3fd)

Solutions

  1. Pick a unique target name (append a suffix/number if needed).
  2. Delete or move the existing target file first if overwrite is truly intended.
  3. Check publicFiles for the target key before calling renameStaticFile and warn the user.
  4. Offer an auto-renamed suggestion when a collision is detected.

Example fix

// before
renameStaticFile({oldRelativePath: 'a.png', newRelativePath: 'b.png'}); // b.png exists

// after
const files = getCanonicalPublicFiles(project);
const target = files['b.png'] ? 'b (1).png' : 'b.png';
renameStaticFile({oldRelativePath: 'a.png', newRelativePath: target});
Defensive patterns

Strategy: validation

Validate before calling

// Before renameStaticFile
const canonical = getCanonicalPublicFiles(project);
const newKey = normalizePublicFilePath(newRelativePath);
if (canonical[newKey] !== undefined) {
  // suggest a unique name
  newRelativePath = uniqueName(newKey, Object.keys(canonical));
}
// or block the action and inform the user

Type guard

const isUniquePublicFile = (project: VirtualProject, path: string): boolean =>
  !Object.prototype.hasOwnProperty.call(getCanonicalPublicFiles(project), normalizePublicFilePath(path));

Try / catch

try {
  await ops.renameStaticFile({oldRelativePath, newRelativePath});
} catch (err) {
  if (String(err?.message ?? '').endsWith('already exists')) {
    showUser('That name is taken; choose another or delete the target first.');
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling renameStaticFile where newRelativePath (after normalization) equals an existing publicFiles key. Trivially reproduced by renaming 'a.png' to 'b.png' when 'b.png' already exists.

Common situations: User picks a name that is already taken; case-only rename on a case-insensitive store causing a collision; UI not checking target existence before submit; attempting to 'rename' onto an existing file to force overwrite.

Related errors


AI-assisted analysis of remotion-dev/remotion@78fe4bb3fd (2026-08-12). Data as JSON: /api/errors/86b3e84636769a55. Report an issue: GitHub.