remotion-dev/remotion · error · Error
Cannot update keyframes without a JSX source location
Error message
Cannot update keyframes without a JSX source location
What it means
getOpeningElementSourceEdit in the keyframes codemod throws this when the JSXOpeningElement has no loc, so the codemod cannot compute the source range of the opening tag to rewrite with updated keyframe props. The keyframes updater works via precise source edits, which require position data on the opening tag.
Solutions
- Re-parse the file with parseAst and resolve the opening element from that AST before updating keyframes
- Re-run the keyframes update on the original file content
- Ensure prior codemod passes preserve node loc information
- Report a Remotion Studio bug if the normal keyframe editor triggers it
Example fix
// before
const opening = buildOpeningElementAst(); // loc: null
getOpeningElementSourceEdit({input, openingElement: opening, prettierConfigOverride, propertyNames});
// after
const ast = parseAst(input);
const opening = findElementAtNodePath(ast, nodePath).node.openingElement;
getOpeningElementSourceEdit({input, openingElement: opening, prettierConfigOverride, propertyNames}); Defensive patterns
Strategy: type-guard
Validate before calling
if (!openingElement.loc) { throw new Error('opening element has no source location; reparse input first'); } Type guard
const hasLoc = (el: JSXOpeningElement): el is JSXOpeningElement & {loc: NonNullable<JSXOpeningElement['loc']>} => el.loc != null; Try / catch
try {
const edit = getOpeningElementSourceEdit({input, openingElement, prettierConfigOverride, propertyNames});
} catch (e) {
if (e instanceof Error && e.message.includes('without a JSX source location')) {
const ast = parseAst(input);
const fresh = findElementAtNodePath(ast, nodePath).node.openingElement;
return getOpeningElementSourceEdit({input, openingElement: fresh, prettierConfigOverride, propertyNames});
}
throw e;
} Prevention
- Resolve the opening element from a fresh parseAst of the current file
- Avoid chained transforms that rebuild elements and drop loc
- Use the Studio keyframe editor on the current file state, not a stale copy
When it happens
Trigger: The keyframe update flow resolves the target element's opening element and its loc is null — the AST node was built programmatically, positions were stripped by an intermediate transform, or the element came from a synthetic AST rather than a parse of the file.
Common situations: Custom tooling invoking the keyframes codemod with builder-created nodes, chained codemods where an earlier pass rebuilt the element, or regressed Studio behavior where the inspected element lost position metadata.
Related errors
- Could not locate the video element
- 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/054fba11c5db2e53.
Report an issue: GitHub.
Appendix: source
Thrown at packages/studio-codemods/src/update-keyframes.ts:105
type SourceFormattingConfig,
} from './source-style';
import {parseValueExpression} from './update-nested-prop';
const b = recast.types.builders;
const getOpeningElementSourceEdit = ({
input,
openingElement,
prettierConfigOverride,
propertyNames,
}: {
input: string;
openingElement: JSXOpeningElement;
prettierConfigOverride: Record<string, unknown> | null;
propertyNames: string[];
}): SourceEdit => {
if (!openingElement.loc) {
throw new Error('Cannot update keyframes without a JSX source location');
}
const start = recastLocToOffset(input, openingElement.loc.start);
const printed = printJsxOpeningElement({
openingElement: openingElement as never,
input,
prettierConfigOverride,
});
return {
start,
end: recastLocToOffset(input, openingElement.loc.end),
replacement: indentContinuationLinesAtOffset({
input,
offset: start,
printed: removeBlankLinesFromUpdatedOpeningElementObjects({
input: printed,
propertyNames,
}),View on GitHub (pinned to 46a3a6bf13)