remotion-dev/remotion · error

Cannot duplicate JSX element with no parent

Error message

Cannot duplicate JSX element with no parent

What it means

getDuplicateSourceEdit must know the parent node to decide how to insert the duplicate (as sibling children, array element, or wrapped in a fragment). If the resolved recast NodePath has no parentPath (i.e. the JSX element is the root of the AST with no enclosing node), there is no valid insertion strategy and it throws.

Solutions

  1. Duplicate an element that is nested inside a parent (component return / fragment) rather than the file root
  2. Verify the SequenceNodePath targets a real nested element with findJsxElementPathForDeletion first
  3. Wrap the element in a fragment or component body in the composition source before duplicating

Example fix

// before (input is only the element)
<Foo/>
// after
export const MyComp = () => (<><Foo/></>);
Defensive patterns

Strategy: validation

Validate before calling

const path = findJsxElementPathForDeletion(ast, nodePath);
if (!path?.parentPath) { throw new Error('target element is the AST root; cannot duplicate'); }

Type guard

const hasParent = (p: recast.types.NodePath | null | undefined) => !!p?.parentPath;

Try / catch

try { await duplicateJsxNodes({input, nodePaths}); } catch (e) { if ((e as Error).message.includes('with no parent')) { /* pick a nested element or wrap in a fragment */ } else throw e; }

Prevention

When it happens

Trigger: Calling duplicateJsxNodes where the captured SequenceNodePath resolves to a JSX element that is the entire file expression with no parent node (e.g. input is exactly `<Foo/>` wrapped by nothing the path can climb to).

Common situations: Minimal single-element test inputs; a SequenceNodePath pointing at the root element of a file rather than an element nested in the component's return statement; stale node paths after code changes.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at packages/studio-codemods/src/duplicate-jsx-node.ts:249

};

const getDuplicateSourceEdit = ({
	input,
	jsxPath,
}: {
	input: string;
	jsxPath: recast.types.NodePath;
}): SourceEdit => {
	const element = jsxPath.node as JSXElement;
	if (!element.loc) {
		throw new Error('Cannot duplicate a JSX element without a source location');
	}

	const start = recastLocToOffset(input, element.loc.start);
	const end = recastLocToOffset(input, element.loc.end);
	const parentNode = jsxPath.parentPath?.node;
	if (!parentNode) {
		throw new Error('Cannot duplicate JSX element with no parent');
	}

	const duplicatedSource = getDuplicatedJsxSource({element, input});
	const indent = getLineIndent({input, offset: start});
	const endOfLine = getEndOfLine(input);
	const lineEnd = input.indexOf('\n', end);
	const sourceAfterElement = input.slice(
		end,
		lineEnd === -1 ? input.length : lineEnd,
	);

	if (parentNode.type === 'JSXElement' || parentNode.type === 'JSXFragment') {
		const isStandalone = sourceAfterElement.trim() === '';
		const lineStart = input.lastIndexOf('\n', start - 1) + 1;
		const whitespaceBeforeElement =
			input.slice(lineStart, start).match(/[\t ]*$/)?.[0] ?? '';
		const insertion = isStandalone
			? `${endOfLine}${indentInsertedJsx({

View on GitHub (pinned to 46a3a6bf13)