{"record":{"id":"e1bf23998e9810d2","repo":"remotion-dev/remotion","slug":"cannot-duplicate-jsx-element-with-no-parent","errorCode":null,"errorMessage":"Cannot duplicate JSX element with no parent","messagePattern":"Cannot duplicate JSX element with no parent","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/studio-codemods/src/duplicate-jsx-node.ts","lineNumber":249,"sourceCode":"};\n\nconst getDuplicateSourceEdit = ({\n\tinput,\n\tjsxPath,\n}: {\n\tinput: string;\n\tjsxPath: recast.types.NodePath;\n}): SourceEdit => {\n\tconst element = jsxPath.node as JSXElement;\n\tif (!element.loc) {\n\t\tthrow new Error('Cannot duplicate a JSX element without a source location');\n\t}\n\n\tconst start = recastLocToOffset(input, element.loc.start);\n\tconst end = recastLocToOffset(input, element.loc.end);\n\tconst parentNode = jsxPath.parentPath?.node;\n\tif (!parentNode) {\n\t\tthrow new Error('Cannot duplicate JSX element with no parent');\n\t}\n\n\tconst duplicatedSource = getDuplicatedJsxSource({element, input});\n\tconst indent = getLineIndent({input, offset: start});\n\tconst endOfLine = getEndOfLine(input);\n\tconst lineEnd = input.indexOf('\\n', end);\n\tconst sourceAfterElement = input.slice(\n\t\tend,\n\t\tlineEnd === -1 ? input.length : lineEnd,\n\t);\n\n\tif (parentNode.type === 'JSXElement' || parentNode.type === 'JSXFragment') {\n\t\tconst isStandalone = sourceAfterElement.trim() === '';\n\t\tconst lineStart = input.lastIndexOf('\\n', start - 1) + 1;\n\t\tconst whitespaceBeforeElement =\n\t\t\tinput.slice(lineStart, start).match(/[\\t ]*$/)?.[0] ?? '';\n\t\tconst insertion = isStandalone\n\t\t\t? `${endOfLine}${indentInsertedJsx({","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/remotion-dev/remotion/blob/46a3a6bf13ca367376ff18bbb06dae2e87e0d068/packages/studio-codemods/src/duplicate-jsx-node.ts#L231-L267","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before (input is only the element)\n<Foo/>\n// after\nexport const MyComp = () => (<><Foo/></>);","handlingStrategy":"validation","validationCode":"const path = findJsxElementPathForDeletion(ast, nodePath);\nif (!path?.parentPath) { throw new Error('target element is the AST root; cannot duplicate'); }","typeGuard":"const hasParent = (p: recast.types.NodePath | null | undefined) => !!p?.parentPath;","tryCatchPattern":"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; }","preventionTips":["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"],"tags":["ast","jsx","parent-node","codemod"],"backgroundTag":"internal-invariant-violation","analyzedSha":"46a3a6bf13ca367376ff18bbb06dae2e87e0d068","analyzedAt":"2026-09-18T00:12:20.256Z","contentChangedAt":"2026-09-18T00:12:20.256Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}