remotion-dev/remotion · error · Error

Could not locate the video element

Error message

Could not locate the video element

What it means

This error is thrown by applyEditToElementSource in the split-video-from-audio codemod when the JSXElement representing the video has no source location (element.loc is null). Without loc offsets the codemod cannot slice the original source around the element, so it refuses to apply the muted-attribute edit. It indicates the AST node was constructed or transformed without position info rather than a problem with the user's code.

Solutions

  1. Verify the element passed to applyEditToElementSource comes directly from parseAst(input) so it retains loc information
  2. Re-run the codemod on the original, untransformed source string
  3. If building AST nodes, set their loc fields or round-trip through parse/print so positions are re-derived
  4. Report a Remotion Studio bug if a normal split-video-from-audio action hits this in the UI

Example fix

// before (synthetic node without loc)
const el = b.jsxElement(b.jsxOpeningElement(b.jsxIdentifier('Video'), []), null, []);
applyEditToElementSource({edit, element: el, input});
// after (parse from source so loc is present)
const ast = parseAst(input);
const el = findJsxElementPathForDeletion(ast, nodePath).node;
applyEditToElementSource({edit, element: el, input});
Defensive patterns

Strategy: type-guard

Validate before calling

const hasLoc = (el: {loc: unknown}): el is {loc: NonNullable<JSXElement['loc']>} => el.loc != null;
if (!hasLoc(element)) throw new Error('element has no source location; reparse input');

Type guard

const hasLoc = (el: JSXElement): el is JSXElement & {loc: NonNullable<JSXElement['loc']>} => el.loc != null;

Try / catch

try {
  const source = applyEditToElementSource({edit, element, input});
} catch (e) {
  if (e instanceof Error && e.message.includes('Could not locate')) {
    const ast = parseAst(input); // reparse and re-resolve the element with loc
    return applyEditToElementSource({edit, element: findElement(ast), input});
  }
  throw e;
}

Prevention

When it happens

Trigger: splitVideoFromAudio is called and the located <Video>/<OffthreadVideo> JSXElement has loc === null — typically because the element was built programmatically via AST builders, an intermediate recast transform stripped positions, or a synthetic node was passed in instead of one parsed from source.

Common situations: Custom callers piping hand-built AST nodes into the codemod internals, running the codemod on code that passed through another transform that dropped source positions, or bugs in codemod preprocessing (e.g. after capturing node paths from a re-created AST).

Related errors


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

Appendix: source

Thrown at packages/studio-codemods/src/split-video-from-audio.ts:242

	const trailingWhitespace = beforeClosing.match(/[\t ]*$/)?.[0] ?? '';
	return {
		start: closingStart - trailingWhitespace.length,
		end: closingStart - trailingWhitespace.length,
		replacement: ' muted',
	};
};

const applyEditToElementSource = ({
	edit,
	element,
	input,
}: {
	edit: SourceEdit;
	element: JSXElement;
	input: string;
}) => {
	if (!element.loc) {
		throw new Error('Could not locate the video element');
	}

	const start = recastLocToOffset(input, element.loc.start);
	const end = recastLocToOffset(input, element.loc.end);
	if (edit.start < start || edit.end > end) {
		throw new Error('Muted attribute edit is outside the video element');
	}

	const source =
		input.slice(start, edit.start) +
		edit.replacement +
		input.slice(edit.end, end);
	const originalIndent = getLineIndent({input, offset: start});
	return source
		.split(/\r?\n/)
		.map((line, index) => {
			if (index === 0) {
				return line;

View on GitHub (pinned to 46a3a6bf13)