n8n-io/n8n · error · ExpressionExtensionError

merge(): expected object arg

Error message

merge(): expected object arg

What it means

Thrown as ExpressionExtensionError by mergeObjects() when its second argument (the object to merge in) is truthy but not of type 'object'. mergeObjects copies keys from `other` into the target only if absent. This guard is defensive: through the public .merge() API the merge() wrapper pre-filters elements with typeof === 'object' checks in both its no-arg reduce path and its array-arg path, so reaching this throw normally means unexpected internal data or a direct call to mergeObjects with a primitive.

Source

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

				newObj[to] = newObj[from];
				// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
				delete newObj[from];
			}
		});
		// eslint-disable-next-line @typescript-eslint/no-unsafe-return
		return newObj;
	});
}

function mergeObjects(value: Record<string, unknown>, extraArgs: unknown[]): unknown {
	const [other] = extraArgs;

	if (!other) {
		return value;
	}

	if (typeof other !== 'object') {
		throw new ExpressionExtensionError('merge(): expected object arg');
	}

	const newObject = { ...value };
	for (const [key, val] of Object.entries(other)) {
		if (!(key in newObject)) {
			newObject[key] = val;
		}
	}
	return newObject;
}

function merge(value: unknown[], extraArgs: unknown[][]): unknown {
	const [others] = extraArgs;

	if (others === undefined) {
		// If there are no arguments passed, merge all objects within the array
		const merged = value.reduce((combined, current) => {
			if (current !== null && typeof current === 'object' && !Array.isArray(current)) {

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Ensure any value merged into an object is itself a plain object (not a primitive).
  2. If calling merge() with an argument array, make sure each element is an object: .merge([{id:1,otherValue:3}]).
  3. Filter the array to objects only before merging: .filter(x => x && typeof x === 'object').
  4. If you hit this from normal usage, report it — the merge() wrapper should shield callers from it.

Example fix

// before — a primitive slips into the merge path
mergeObjects(target, [42]); // 42 is truthy and not an object -> throws
// after — merge an object
mergeObjects(target, [{ extra: 1 }]);
// in an expression, keep elements as objects
{{ $json.rows.merge([{ id: 1, otherValue: 3 }]) }}
Defensive patterns

Strategy: validation

Validate before calling

function mergeObjectsSafe(target, other) {
  if (other && typeof other !== 'object') throw new TypeError('merge(): expected object arg');
  const out = { ...target }; for (const [k, v] of Object.entries(other)) if (!(k in out)) out[k] = v; return out;
}

Type guard

function isMergeableObject(v) { return v !== null && v !== undefined && typeof v === 'object'; }

Try / catch

try {
  result = arr.merge([{ id: 1, otherValue: 3 }]);
} catch (e) {
  if (e.name === 'ExpressionExtensionError' && /merge.*object/i.test(e.message)) {
    // ensure each element being merged is a plain object, not a primitive
  }
}

Prevention

When it happens

Trigger: Directly invoking mergeObjects with a primitive second argument (number, string, boolean) that is truthy. Indirectly, only if the merge() wrapper's typeof guards are bypassed by malformed input that slips through — e.g. an element that is a primitive where the wrapper expected an object. In typical {{ [...]().merge(...) }} usage this is hard to hit.

Common situations: An internal caller or test invoking mergeObjects directly with a non-object. A future refactor that weakens the merge() wrapper's pre-filtering. Edge data where an object field is unexpectedly a primitive.

Related errors


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