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
- Ensure any value merged into an object is itself a plain object (not a primitive).
- If calling merge() with an argument array, make sure each element is an object: .merge([{id:1,otherValue:3}]).
- Filter the array to objects only before merging: .filter(x => x && typeof x === 'object').
- 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
- Ensure every value merged into an object is itself a plain object.
- Filter arrays to objects before merging: .filter(x => x && typeof x === 'object').merge(...).
- Pass merge arguments as arrays of objects: .merge([{id:1,otherValue:3}]).
- Report it if reached through normal .merge() usage — the wrapper should shield callers.
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
- merge(): expected array arg, e.g. .merge([{ id: 1, otherValu
- arguments must be passed to pluck
- smartJoin(): expected two string args, e.g. .smartJoin("name
- chunk(): expected non-zero numeric arg, e.g. .chunk(5)
- renameKeys(): expected an even amount of args: from1, to1 [,
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/5ce73c0620412bd8.
Report an issue: GitHub.