n8n-io/n8n · error · ExpressionExtensionError

difference(): expected array arg, e.g. .difference([1, 2, 3,

Error message

difference(): expected array arg, e.g. .difference([1, 2, 3, 4])

What it means

Thrown as ExpressionExtensionError by the .difference() array extension when its argument is not an array. difference() returns elements in the base array that are not present in the other array; a non-array argument cannot be scanned for membership, so it is rejected up front.

Source

Thrown at packages/@n8n/expression-runtime/src/extensions/array-extensions.ts:286

function union(value: unknown[], extraArgs: unknown[][]): unknown[] {
	const [others] = extraArgs;
	if (!Array.isArray(others)) {
		throw new ExpressionExtensionError('union(): expected array arg, e.g. .union([1, 2, 3, 4])');
	}
	const newArr: unknown[] = Array.from(value);
	for (const v of others) {
		if (newArr.findIndex((w) => isEqual(w, v)) === -1) {
			newArr.push(v);
		}
	}
	return unique(newArr, []);
}

function difference(value: unknown[], extraArgs: unknown[][]): unknown[] {
	const [others] = extraArgs;
	if (!Array.isArray(others)) {
		throw new ExpressionExtensionError(
			'difference(): expected array arg, e.g. .difference([1, 2, 3, 4])',
		);
	}
	const newArr: unknown[] = [];
	for (const v of value) {
		if (others.findIndex((w) => isEqual(w, v)) === -1) {
			newArr.push(v);
		}
	}
	return unique(newArr, []);
}

function intersection(value: unknown[], extraArgs: unknown[][]): unknown[] {
	const [others] = extraArgs;
	if (!Array.isArray(others)) {
		throw new ExpressionExtensionError(
			'intersection(): expected array arg, e.g. .intersection([1, 2, 3, 4])',
		);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Pass an array: .difference([1,2,3,4]).
  2. If the argument is dynamic, ensure it is an array before calling: Array.isArray(arg) ? .difference(arg) : .difference([arg]).
  3. Coerce single values into a one-element array when a difference with a scalar is intended.
  4. Confirm the upstream expression yields an array.

Example fix

// before — scalar argument
{{ $json.ids.difference($json.excluded) }} // if excluded is a number, throws
// after — ensure array argument
{{ $json.ids.difference(Array.isArray($json.excluded) ? $json.excluded : [$json.excluded]) }}
Defensive patterns

Strategy: type-guard

Validate before calling

function differenceSafe(arr, others) {
  if (!Array.isArray(others)) throw new TypeError('difference(): expected array arg');
  return arr.filter(x => !others.includes(x));
}

Type guard

function isDifferenceArgValid(others) { return Array.isArray(others); }

Try / catch

try {
  result = arr.difference(others);
} catch (e) {
  if (e.name === 'ExpressionExtensionError' && /difference.*array/i.test(e.message)) {
    // ensure others is an array: arr.difference(Array.isArray(others) ? others : [others])
  }
}

Prevention

When it happens

Trigger: Calling .difference() with a non-array: .difference(5), .difference('abc'), .difference({a:1}), or with undefined where the harness passes a non-array. The expected form is .difference([1,2,3,4]).

Common situations: Passing a scalar or object instead of an array. A template variable resolving to a single value. Forgetting the brackets. Same shape as the union/intersection guards.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/ac8ec15ae2d84355. Report an issue: GitHub.