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

  1. Re-parse the composition file after any generation step so the element has fresh source locations before splitting
  2. Only split elements that came directly from the parsed file
  3. 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

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


AI-assisted analysis of remotion-dev/remotion@b2f4e34732 (2026-09-02). Data as JSON: /api/errors/71dff61864aeef1b. Report an issue: GitHub.