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

  1. Re-parse the file with parseAst and resolve the opening element from that AST before updating keyframes
  2. Re-run the keyframes update on the original file content
  3. Ensure prior codemod passes preserve node loc information
  4. 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

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


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)