remotion-dev/remotion · error

Cannot update effect prop

Error message

Cannot update effect prop: ${found.reason}

What it means

After locating the effects attribute, updateEffectPropsAst() calls findEffectCallExpression({attr, effectIndex}) to find the effect call at effectIndex. If that lookup fails (index out of range, entry not a call expression, etc.) it returns {kind: 'error', reason} and the library surfaces the reason in this templated error message. The actual reason is appended after 'Cannot update effect prop: '.

Solutions

  1. Re-enumerate the effects on the target element and re-map UI rows to fresh indices before updating.
  2. Clamp/validate effectIndex against the current number of call-expression effects.
  3. Refresh editor state after any reorder/delete/add codemod so indices stay in sync.

Example fix

// before
await updateEffectProps({input, effectIndex: staleIndex, sequenceNodePath, update, schema});
// after
const effects = listEffects(input, sequenceNodePath); // fresh enumeration
if (staleIndex >= effects.length) return;
await updateEffectProps({input, effectIndex: staleIndex, sequenceNodePath, update, schema});
Defensive patterns

Strategy: validation

Validate before calling

const effects = enumerateEffects(input, sequenceNodePath)
  .filter((el) => el.kind === 'call');
if (effectIndex < 0 || effectIndex >= effects.length) {
  throw new RangeError(`effectIndex ${effectIndex} out of range`);
}

Type guard

const hasEffectAtIndex = (index: number, effects: EffectArrayElement[]) =>
  Number.isInteger(index) && index >= 0 && index < effects.length && effects[index].kind === 'call';

Try / catch

try {
  await updateEffectProps({input, effectIndex, sequenceNodePath, update, schema});
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Cannot update effect prop:')) {
    // re-enumerate effects and rebind indices, then retry
  } else throw e;
}

Prevention

When it happens

Trigger: Calling updateEffectProps with an effectIndex beyond the array length; effectIndex pointing at a spread/non-call element; index captured from a stale effect list after the file was edited (effects added/deleted/reordered).

Common situations: Property panel bound to an index that changed after a drag-reorder; concurrent edits to the source file invalidating indices; off-by-one when the panel counts effects including non-call entries.

Related errors


AI-assisted analysis of remotion-dev/remotion@46a3a6bf13 (2026-09-18). Data as JSON: /api/errors/ac658d0ce4285d60. Report an issue: GitHub.

Appendix: source

Thrown at packages/studio-codemods/src/effect-operations.ts:1035

	serialized: string;
	oldValueString: string;
	newValueString: string;
	logLine: number;
	effectCallee: string;
	removedProps: PropDelta[];
} => {
	const ast = parseAst(input);
	const snapshots = captureEffectSourceSnapshots(ast);
	const jsx = getJsx({action: 'update', ast, sequenceNodePath});
	trackOpeningElement({jsx, snapshots});
	const attr = findEffectsAttr(jsx.attributes);
	if (!attr) {
		throw new Error('Could not find effects on the target JSX element');
	}

	const found = findEffectCallExpression({attr, effectIndex});
	if (found.kind === 'error') {
		throw new Error(`Cannot update effect prop: ${found.reason}`);
	}

	const {call, callee: effectCallee} = found;
	const isDefault =
		!isEffectParamUpdate(update) &&
		update.defaultValue !== null &&
		JSON.stringify(update.value) === JSON.stringify(update.defaultValue);
	let object: ObjectExpression;
	if (call.arguments.length === 0) {
		if (isDefault) {
			return {
				serialized: input,
				oldValueString: '',
				newValueString: JSON.stringify(update.defaultValue),
				logLine: call.loc?.start.line ?? jsx.loc?.start.line ?? 1,
				effectCallee,
				removedProps: [],
			};

View on GitHub (pinned to 46a3a6bf13)