{"record":{"id":"054fba11c5db2e53","repo":"remotion-dev/remotion","slug":"cannot-update-keyframes-without-a-jsx-source-location","errorCode":null,"errorMessage":"Cannot update keyframes without a JSX source location","messagePattern":"Cannot update keyframes without a JSX source location","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/studio-codemods/src/update-keyframes.ts","lineNumber":105,"sourceCode":"\ttype SourceFormattingConfig,\n} from './source-style';\nimport {parseValueExpression} from './update-nested-prop';\n\nconst b = recast.types.builders;\n\nconst getOpeningElementSourceEdit = ({\n\tinput,\n\topeningElement,\n\tprettierConfigOverride,\n\tpropertyNames,\n}: {\n\tinput: string;\n\topeningElement: JSXOpeningElement;\n\tprettierConfigOverride: Record<string, unknown> | null;\n\tpropertyNames: string[];\n}): SourceEdit => {\n\tif (!openingElement.loc) {\n\t\tthrow new Error('Cannot update keyframes without a JSX source location');\n\t}\n\n\tconst start = recastLocToOffset(input, openingElement.loc.start);\n\tconst printed = printJsxOpeningElement({\n\t\topeningElement: openingElement as never,\n\t\tinput,\n\t\tprettierConfigOverride,\n\t});\n\treturn {\n\t\tstart,\n\t\tend: recastLocToOffset(input, openingElement.loc.end),\n\t\treplacement: indentContinuationLinesAtOffset({\n\t\t\tinput,\n\t\t\toffset: start,\n\t\t\tprinted: removeBlankLinesFromUpdatedOpeningElementObjects({\n\t\t\t\tinput: printed,\n\t\t\t\tpropertyNames,\n\t\t\t}),","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/remotion-dev/remotion/blob/46a3a6bf13ca367376ff18bbb06dae2e87e0d068/packages/studio-codemods/src/update-keyframes.ts#L87-L123","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nconst opening = buildOpeningElementAst(); // loc: null\ngetOpeningElementSourceEdit({input, openingElement: opening, prettierConfigOverride, propertyNames});\n// after\nconst ast = parseAst(input);\nconst opening = findElementAtNodePath(ast, nodePath).node.openingElement;\ngetOpeningElementSourceEdit({input, openingElement: opening, prettierConfigOverride, propertyNames});","handlingStrategy":"type-guard","validationCode":"if (!openingElement.loc) { throw new Error('opening element has no source location; reparse input first'); }","typeGuard":"const hasLoc = (el: JSXOpeningElement): el is JSXOpeningElement & {loc: NonNullable<JSXOpeningElement['loc']>} => el.loc != null;","tryCatchPattern":"try {\n  const edit = getOpeningElementSourceEdit({input, openingElement, prettierConfigOverride, propertyNames});\n} catch (e) {\n  if (e instanceof Error && e.message.includes('without a JSX source location')) {\n    const ast = parseAst(input);\n    const fresh = findElementAtNodePath(ast, nodePath).node.openingElement;\n    return getOpeningElementSourceEdit({input, openingElement: fresh, prettierConfigOverride, propertyNames});\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["ast","codemod","source-location","keyframes"],"backgroundTag":"missing-source-location","analyzedSha":"46a3a6bf13ca367376ff18bbb06dae2e87e0d068","analyzedAt":"2026-09-18T00:12:20.256Z","contentChangedAt":"2026-09-18T00:12:20.256Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}