remotion-dev/remotion · error

Cannot duplicate effect

Error message

Cannot duplicate effect: ${found.reason}

What it means

duplicateEffects validates each requested effect via findEffectCallExpression, which returns a discriminated result. When the result is an error ({kind:'error'}), the reason (e.g. 'not-found', 'out-of-range', 'not-call-expression') is interpolated into this message and thrown. It means the effectIndex does not resolve to a usable effect call in the element's effects array.

Solutions

  1. Re-read the element and enumerate its effects array to get fresh indexes before calling
  2. Verify effectIndex is within 0..effects.length-1 for the resolved element
  3. Remove or normalize non-CallExpression entries in the effects array
  4. Use the most recent effect listing (e.g. from the composition parse) as the index source

Example fix

// before
duplicateEffect({effectIndex: 3, ...}); // array has 2 entries
// after
const idx = effectsArray.length - 1; duplicateEffect({effectIndex: idx, ...});
Defensive patterns

Strategy: validation

Validate before calling

const el = findJsxElementAtNodePath(parseAst(input), sequenceNodePath);
const arr = getEffectsArray(findEffectsAttr(el!.openingElement.attributes)!, 'check');
if (effectIndex < 0 || effectIndex >= arr.elements.length) throw new Error('effectIndex out of range');

Type guard

const isValidEffectIndex = (n: number, len: number): boolean => Number.isInteger(n) && n >= 0 && n < len;

Try / catch

try { await duplicateEffect({effectIndex, input, sequenceNodePath}); } catch (e) { if ((e as Error).message.startsWith('Cannot duplicate effect:')) { /* refresh indices from a fresh parse and retry */ } else throw e; }

Prevention

When it happens

Trigger: duplicateEffects/duplicateEffect called with an effectIndex that does not exist in the element's effects array, indexes a non-call element (spread/conditional), or refers to a stale index after the array was modified.

Common situations: Stale effect indexes captured in Studio state before the file changed out-of-band; counting effects from a different element than the one the nodePath resolves to; effects array containing non-call entries.

Related errors


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

Appendix: source

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

			JSXAttribute,
			{
				array: ArrayExpression;
				indices: Set<number>;
				effectLabels: string[];
				logLines: number[];
			}
		>();
		for (const {effectIndex, sequenceNodePath} of effects) {
			const jsx = getJsx({action: 'duplicate', ast, sequenceNodePath});
			const attr = findEffectsAttr(jsx.attributes);
			if (!attr) {
				throw new Error('Could not find effects on the target JSX element');
			}

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

			const group = effectsByAttribute.get(attr) ?? {
				array,
				indices: new Set<number>(),
				effectLabels: [],
				logLines: [],
			};
			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,

View on GitHub (pinned to 46a3a6bf13)