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
- Verify the element passed to applyEditToElementSource comes directly from parseAst(input) so it retains loc information
- Re-run the codemod on the original, untransformed source string
- If building AST nodes, set their loc fields or round-trip through parse/print so positions are re-derived
- 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
- Always resolve AST nodes from parseAst(input), never from builder-built synthetic nodes
- Avoid mutating the AST between parsing and edit application
- Round-trip programmatically generated code through print+parse to restore loc
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
- Cannot update keyframes without a JSX source location
- Cannot duplicate JSX element with no parent
- Cannot split a JSX sequence without a source location
- Cannot split audio from a JSX element with no parent
- Cannot split JSX sequence with no parent
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)