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
- Duplicate an element that is nested inside a parent (component return / fragment) rather than the file root
- Verify the SequenceNodePath targets a real nested element with findJsxElementPathForDeletion first
- 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
- Target elements nested in a component return/fragment, never the file root
- Refresh SequenceNodePath against current source before duplicating
- Ensure compositions wrap elements in a component body
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
- Cannot split a JSX sequence without a source location
- Cannot split JSX sequence with no parent
- Could not find a root JSX element
- Could not find a root JSX element
- Could not find as a direct JSX child
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)