remotion-dev/remotion · error · Error

Could not delete JSX nodes

Error message

Could not delete JSX nodes

What it means

deleteJsxNode commits the deletion through controller.applyMutation, which returns null when the mutation does not actually change the project (mutate(before) returned the identical project reference, i.e. a no-op commit with no node-path mutation to report). deleteJsxNode treats a null result as failure and throws; the catch converts it to {success: false, reason}.

Source

Thrown at packages/browser-studio/src/browser-studio-operations.ts:1320

				...project,
				files: {
					...project.files,
					...Object.fromEntries(
						updates.map(({fileName, result}) => [fileName, result.output]),
					),
				},
			};
			const nodePathMutation = controller.applyMutation({
				timelineSelection: null,
				fileName: updates.map(({fileName}) => fileName).join(', '),
				mutate: () => nextProject,
				nodePathMutationFiles: updates.map(({fileName, result}) => ({
					absolutePath: fileName,
					remappings: result.nodePathRemappings,
				})),
			});
			if (nodePathMutation === null) {
				throw new Error('Could not delete JSX nodes');
			}

			return {success: true, nodePathMutation};
		} catch (error) {
			return {
				success: false,
				reason: error instanceof Error ? error.message : String(error),
				stack: error instanceof Error && error.stack ? error.stack : '',
			};
		}
	};

	const duplicateJsxNode: BrowserStudioOperations['duplicateJsxNode'] = async ({
		nodes,
	}) => {
		try {
			if (nodes.length === 0) {
				throw new Error('No JSX nodes were specified for duplication');

View on GitHub (pinned to b2f4e34732)

Solutions

  1. Verify each nodePath still resolves in the current file contents before calling deleteJsxNode.
  2. Refresh node paths from 'sequence-node-paths-remapped' events after any mutation that remaps them.
  3. Retry the operation once after re-reading the project; if it reproduces, capture the file source and node paths and report it as a bug.
Defensive patterns

Strategy: validation

Validate before calling

const nodePathResolves = (source: string, nodePath: readonly number[]): boolean => {
  // cheap structural check: file must contain JSX and the parent path must be in range
  return source.length > 0 && nodePath.every((index) => Number.isInteger(index) && index >= 0);
};

const staleNodes = nodes.filter((n) => !nodePathResolves(project.files[n.fileName] ?? '', n.nodePath.nodePath));
if (staleNodes.length === nodes.length) {
  // all paths stale — refresh from 'sequence-node-paths-remapped' before deleting
}

Try / catch

const result = await operations.deleteJsxNode({nodes});
if (!result.success && result.reason === 'Could not delete JSX nodes') {
  // node paths are stale: refresh them from remap events and rebuild the request
}

Prevention

When it happens

Trigger: Deleting JSX nodes whose codemod output matches the existing file content (stale node paths pointing at nothing), or racing a concurrent project replacement so the committed mutation is a no-op and applyMutation yields null.

Common situations: Node paths captured before an edit or undo shifted the JSX; automated agents replaying delete operations against a changed project; rare defensive path — reproducible occurrences usually indicate stale node-path state.

Related errors


AI-assisted analysis of remotion-dev/remotion@b2f4e34732 (2026-08-22). Data as JSON: /api/errors/4f686148a005998b. Report an issue: GitHub.