remotion-dev/remotion · error
Cannot split a JSX sequence without a source location
Error message
Cannot split a JSX sequence without a source location
What it means
getSplitSourceEdit computes a source text edit to split a JSX sequence by slicing the original source around the 'left' JSX element. Recast attaches source locations only to elements that were parsed from source (not synthesized nodes). If left.loc is missing, the element's position in the original file is unknown and the split cannot produce a text edit, so this error is thrown.
Source
Thrown at packages/studio-codemods/src/split-jsx-sequence.ts:362
const endOfLine = source.includes('\r\n') ? '\r\n' : '\n';
return source.split(/\r?\n/).join(`${endOfLine}${indent}`);
};
const getSplitSourceEdit = ({
input,
left,
prettierConfigOverride,
right,
wrapInFragment,
}: {
input: string;
left: JSXElement;
prettierConfigOverride: Record<string, unknown> | null;
right: JSXElement;
wrapInFragment: boolean;
}) => {
if (!left.loc) {
throw new Error('Cannot split a JSX sequence without a source location');
}
const start = recastLocToOffset(input, left.loc.start);
const end = recastLocToOffset(input, left.loc.end);
const lineStart = input.lastIndexOf('\n', start - 1) + 1;
const beforeElement = input.slice(lineStart, start);
const lineIndent = beforeElement.match(/^\s*/)?.[0] ?? '';
const isOnlyElementOnLine = beforeElement.trim() === '';
const endOfLine = input.includes('\r\n') ? '\r\n' : '\n';
const formattingConfig = prettierConfigOverride ?? null;
const print = (element: JSXElement | JSXFragment) =>
indentContinuationLines({
indent: lineIndent,
source: printInsertedJsx({
element: element as unknown as
| AstNamedTypes.JSXElement
| AstNamedTypes.JSXFragment,
input,View on GitHub (pinned to b2f4e34732)
Solutions
- Re-parse the composition file after any generation step so the element has fresh source locations before splitting
- Only split elements that came directly from the parsed file
- Skip the split for elements lacking .loc and handle them via full-file rewrite instead
Example fix
// before const ast = transform(generateElement()); // synthesized, no .loc split(ast); // after const ast = parse(originalSource); // re-read from disk after prior writes split(ast);
Defensive patterns
Strategy: type-guard
Validate before calling
if (!left.loc) {
// re-parse from source so recast attaches locations, then retry
ast = parse(originalSource);
} Type guard
const hasLoc = (el: JSXElement): el is JSXElement & {loc: NonNullable<JSXElement['loc']>} => el.loc !== null && el.loc !== undefined; Try / catch
try {
edit = getSplitSourceEdit({left, ...});
} catch (e) {
if (String(e).includes('without a source location')) {
// re-parse file and re-resolve the element before splitting
} else throw e;
} Prevention
- Only split elements that originate from the parsed source file, not synthesized nodes
- Re-parse the composition after any codegen step before running further codemods
- Check .loc on nodes before computing text edits
When it happens
Trigger: Calling splitJsxSequence/sourceEdit where the left JSX element has no .loc — e.g. the element was programmatically synthesized by a previous codemod/recast transformation and printed without source positions, rather than originating from the parsed file.
Common situations: Chained codemods where a generated element is split in a second pass; running a split on AST nodes produced by replaceWith() instead of freshly parsed source.
Related errors
- Could not find a root JSX element
- Could not find ${getCompositionOrFolderLabel(transformation.
- Cannot split JSX sequence with no parent
- Could not find ${getCompositionOrFolderLabel(transformation.
- Could not find folder "${folderPath}"
AI-assisted analysis of remotion-dev/remotion@b2f4e34732 (2026-09-02).
Data as JSON: /api/errors/71dff61864aeef1b.
Report an issue: GitHub.