remotion-dev/remotion · error · Error
Cannot split audio from a JSX element with no parent
Error message
Cannot split audio from a JSX element with no parent
What it means
splitVideoFromAudio throws this when the target JSXElement's parentPath is null. To extract audio it must either insert the <Audio> sibling after the element in its parent, or wrap both in a fragment — neither is possible without a parent node. It indicates an AST invariant violation rather than a user-input problem.
Solutions
- Obtain the element path from a fresh parseAst(input) call and use it before mutating the AST
- Never mutate the AST between resolving the path and calling splitVideoFromAudio
- Pass a nodePath from captureJsxNodePaths on the same input string
- Report a bug if the Studio split action hits this on unmodified code
Example fix
// before (detached node)
const detachedPath = {node: someElement, parentPath: null};
splitVideoFromAudio({input, nodePath: detachedPath});
// after (fresh, attached path)
const ast = parseAst(input);
const jsxPath = findJsxElementPathForDeletion(ast, nodePath);
splitVideoFromAudio({input, nodePath: jsxPath}); Defensive patterns
Strategy: try-catch
Validate before calling
const ast = parseAst(input);
const jsxPath = findJsxElementPathForDeletion(ast, nodePath);
if (!jsxPath || !jsxPath.parentPath) throw new Error('target element must be attached to a parent in the AST'); Type guard
const hasParent = (p: {parentPath: unknown}): p is {parentPath: NonNullable<typeof p.parentPath>} => p.parentPath != null; Try / catch
try {
const result = await splitVideoFromAudio({input, nodePath});
} catch (e) {
if (e instanceof Error && e.message.includes('no parent')) {
// resolve a fresh path from a fresh parse; detached paths cannot be repaired
const fresh = findJsxElementPathForDeletion(parseAst(input), nodePath);
return splitVideoFromAudio({input, nodePath: fresh});
}
throw e;
} Prevention
- Never mutate or detach AST nodes before calling splitVideoFromAudio
- Use paths returned by captureJsxNodePaths on the same input string
- Don't pass hand-built path objects in custom tooling
When it happens
Trigger: The resolved AST path for the JSX element has no parent, e.g. the element was treated as the program root, a synthetic detached node was passed, or path bookkeeping was broken before the parent lookup.
Common situations: Passing a node path whose node was removed/replaced in the AST, hand-constructed paths in custom tooling, or codemod bugs after prior in-place mutations invalidated the path.
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
- Expected the JSX name attribute to be a string
- Cannot duplicate effect: not-call-expression
- Cannot duplicate JSX element with no parent
- Cannot split a JSX sequence without a source location
- Cannot split JSX sequence with no parent
AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18).
Data as JSON: /api/errors/e74405d7bfc4ddcb.
Report an issue: GitHub.
Appendix: source
Thrown at packages/studio-codemods/src/split-video-from-audio.ts:491
return clone;
}),
selfClosing: true,
},
closingElement: null,
children: [],
};
const audioSource = printInsertedJsx({
element: audioElement as never,
input,
originalAttributeSources,
prettierConfigOverride: formattingConfig,
});
setBareMutedAttribute(jsxElement);
const {parentPath} = jsxPath;
if (!parentPath) {
throw new Error('Cannot split audio from a JSX element with no parent');
}
const insertedAfter = insertAfter(parentPath.node, jsxElement, audioElement);
if (!insertedAfter) {
jsxPath.replace(makeFragment(jsxElement, audioElement));
}
const insertedAsJsxSibling =
insertedAfter &&
(parentPath.node.type === 'JSXElement' ||
parentPath.node.type === 'JSXFragment');
const output = applySourceEdits({
input,
edits: [
...getInsertImportSourceEdits({
ast,
input,
prettierConfigOverride: formattingConfig,View on GitHub (pinned to 46a3a6bf13)