{"record":{"id":"cc8c5c0fe018da32","repo":"remotion-dev/remotion","slug":"cannot-update-effect-without-a-jsx-source-location","errorCode":null,"errorMessage":"Cannot update effect without a JSX source location","messagePattern":"Cannot update effect without a JSX source location","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/studio-codemods/src/effect-operations.ts","lineNumber":228,"sourceCode":"\t\t\t\tconst blockStart = recastLocToOffset(input, body.loc.start);\n\t\t\t\tconst blockEnd = recastLocToOffset(input, body.loc.end);\n\t\t\t\treturn (\n\t\t\t\t\tblockStart < offset &&\n\t\t\t\t\toffset < blockEnd &&\n\t\t\t\t\tinput.lastIndexOf('\\n', blockStart - 1) + 1 === lineStart\n\t\t\t\t);\n\t\t\t},\n\t\t);\n\t\tconst lineIndent = getLineIndent({input, offset});\n\t\treturn startsInsideSameLineFunctionBlock\n\t\t\t? `${lineIndent}${getIndentationUnit(input, prettierConfigOverride)}`\n\t\t\t: lineIndent;\n\t};\n\n\tconst openingElementEdits: SourceEdit[] = [];\n\tfor (const [openingElement, location] of snapshots.openingElementLocations) {\n\t\tif (!location) {\n\t\t\tthrow new Error('Cannot update effect without a JSX source location');\n\t\t}\n\n\t\tconst start = recastLocToOffset(input, location.start);\n\t\tconst end = recastLocToOffset(input, location.end);\n\t\tif (isCoveredByFunctionEdit(start, end)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst original = input.slice(start, end);\n\t\tif (recast.print(openingElement).code === original) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst replacement = indentContinuationLines({\n\t\t\tindent: getJsxSourceIndent(start),\n\t\t\tinput,\n\t\t\tprinted: printJsxOpeningElement({\n\t\t\t\topeningElement: openingElement as never,","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/remotion-dev/remotion/blob/46a3a6bf13ca367376ff18bbb06dae2e87e0d068/packages/studio-codemods/src/effect-operations.ts#L210-L246","documentation":"getEffectSourceOutput rewrites opening JSX elements that were modified (new/changed `effects` prop) by replacing the original source text at the recorded location. Each tracked opening element must have a source location; if a snapshot's location is null the codemod cannot compute the text range to patch and throws. It is an invariant guard protecting the edit pipeline used by addEffect, duplicateEffects, reorderEffect, deleteEffects, pasteEffects and updateEffectPropsAst.","triggerScenarios":"Any effect operation (addEffect, duplicateEffects, reorderEffect, deleteEffects, pasteEffects, updateEffectPropsAst) on input whose JSX opening elements lack `loc` — e.g. the AST was parsed without locations or the opening element node was synthesized without positions.","commonSituations":"Passing machine-generated JSX source into effect operations; parsing with a parser configuration that strips location data; an AST that was transformed by other tooling before captureEffectSourceSnapshots.","solutions":["Pass the original source string as `input` so parseAst produces nodes with locations","Avoid mutating/rebuilding the JSX AST before calling the effect operation","Verify `jsx.loc` / openingElement.loc is non-null before invoking; skip or re-parse otherwise"],"exampleFix":"// before\nconst ast = parse(input, {loc: false}); addEffect({input, ...})\n// after\naddEffect({input: originalSource, ...}) // parsed internally with locations","handlingStrategy":"validation","validationCode":"for (const el of allOpeningElements(ast)) {\n  if (!el.loc) throw new Error('opening element missing source location; re-parse original source');\n}","typeGuard":"const located = (el: JSXOpeningElement | null | undefined): el is JSXOpeningElement & {loc: SourceLocation} => !!el?.loc;","tryCatchPattern":"try { await addEffect({...}); } catch (e) { if ((e as Error).message.includes('without a JSX source location')) { /* re-run with the pristine source string */ } else throw e; }","preventionTips":["Pass original source, never AST-only input","Don't strip `loc` via parser options or node cloning","Run effect operations on freshly parsed files"],"tags":["ast","jsx","effects","source-location"],"backgroundTag":"missing-required-argument","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"}