remotion-dev/remotion · error

Cannot duplicate effect: not-call-expression

Error message

Cannot duplicate effect: not-call-expression

What it means

After grouping effects to duplicate, duplicateEffects performs a final invariant check that each array slot being cloned is a CallExpression (an effect call like LightLeak({...})). If the slot is null, a spread, conditional, or other node it cannot clone it and throws. This is the second, defensive line after findEffectCallExpression already filtered candidates.

Solutions

  1. Rewrite the effects array so every entry is a plain effect CallExpression
  2. Duplicate a different index that points at a call expression
  3. Skip indices resolving to non-call elements before calling duplicateEffects
  4. Update the effect via addEffect/delete + add if the source entry is a complex expression

Example fix

// before
effects={[...(getEffects()), LightLeak({})]}
// after
effects={[getEffects()[0], LightLeak({})]} // or plain calls only
Defensive patterns

Strategy: validation

Validate before calling

for (const el of arr.elements) {
  if (el && el.type !== 'CallExpression') throw new Error('effects array contains a non-call entry');
}

Type guard

const isCallElement = (el: (Node | null)[] extends never ? never : Node | null | undefined): el is CallExpression => !!el && el.type === 'CallExpression';

Try / catch

try { await duplicateEffects({input, effects}); } catch (e) { if ((e as Error).message.endsWith('not-call-expression')) { /* normalize the effects array, then retry */ } else throw e; }

Prevention

When it happens

Trigger: An effects array containing non-call entries (e.g. {...spread}, cond ? A({}) : B({}), comments/nulls) at an index selected for duplication; a race where the array was mutated between validation and the splice loop.

Common situations: Hand-edited effects props with conditional or spread entries; older source written before strict call-only arrays were enforced; concurrent edits changing the array during the operation.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

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

			};
			effectsByAttribute.set(attr, group);
			trackOpeningElement({jsx, snapshots});
			if (group.indices.has(effectIndex)) {
				continue;
			}

			group.indices.add(effectIndex);
			group.effectLabels.push(`${found.callee}()`);
			group.logLines.push(
				found.call.loc?.start.line ?? jsx.loc?.start.line ?? 1,
			);
		}

		for (const {array, indices} of effectsByAttribute.values()) {
			for (const effectIndex of [...indices].sort((a, bValue) => bValue - a)) {
				const effect = array.elements[effectIndex];
				if (!effect || effect.type !== 'CallExpression') {
					throw new Error('Cannot duplicate effect: not-call-expression');
				}

				array.elements.splice(
					effectIndex + 1,
					0,
					cloneAstValue(effect) as never,
				);
			}
		}

		return {
			output: getEffectSourceOutput({
				ast,
				input,
				prettierConfigOverride: prettierConfigOverride ?? null,
				snapshots,
			}),
			formatted: true,

View on GitHub (pinned to 46a3a6bf13)