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

  1. Obtain the element path from a fresh parseAst(input) call and use it before mutating the AST
  2. Never mutate the AST between resolving the path and calling splitVideoFromAudio
  3. Pass a nodePath from captureJsxNodePaths on the same input string
  4. 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

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


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)