Your Code node is in Run Once for All Items mode and returned something that isn't an array of objects. n8n checks the shape of the return value before it looks at any of your data, so this fires even when the data inside is perfect.
The description under it always reads: "Please return an array of objects, one for each item you would like to output."
Two conditions, and only two. Both come straight from n8n's validator:
The return value isn't an object at all. A string, a number, a boolean — or
nothing, because a path through your code hit the end of the function without a
return. That last one is the most common cause by a distance, and it's invisible
when you read the code, because the return you're looking at is inside an if.
You returned an array, but at least one element isn't an object.
return [1, 2, 3] and return ['a', 'b'] both fail here. So does an array with a
null hiding in it from a .map() that didn't cover every branch.
One consequence worth knowing: typeof null === 'object' in JavaScript, so
return null passes this check and fails further down with a different message.
If you're staring at this error, null is not what you returned.
The shape n8n wants is an array of objects, each with a json key:
return $input.all().map(item => ({
json: {
email: item.json.email,
domain: item.json.email.split('@')[1],
},
}));
Three checks that resolve almost every occurrence:
return [] as the last line of the function. An
empty array is a valid, explicit "no output"..forEach()? forEach returns undefined.
You want .map(), or push into an array and return that.await go missing? Returning a Promise fails this check. await it.n8n will wrap a bare object in json for you — but only under a specific condition,
and getting it half-right is what produces the confusing failures.
n8n reserves exactly five top-level item keys:
json binary pairedItem error index
The rule:
json for you. So return [{ name: 'Ada' }] works fine and becomes
{ json: { name: 'Ada' } }.json.So this is fine:
return [{ name: 'Ada', role: 'admin' }]; // wrapped for you
and this throws — not with the error at the top of this page, but with Invalid output format, described as "An output item contains the reserved key json. To
get around this, please wrap each item in an object, under a key called json.":
return [{ json: { name: 'Ada' }, role: 'admin' }]; // mixed. refused.
The fix is to put the stray key where it belongs:
return [{ json: { name: 'Ada', role: 'admin' } }];
If no reserved key is present but an unknown one is flagged, the message is
Unknown top-level item key: <yourKey>, described as "Access the properties of an
item under .json, e.g. item.json" — same mistake, caught on a different path.
If you're in Run Once for Each Item mode, you get different wording, and the wording tells you which mistake you made:
| Message | You did this |
|---|---|
Code doesn't return an object |
returned a string/number/undefined from per-item mode |
Code doesn't return a single object |
returned an array from per-item mode |
The second one's description says it outright: "If you need to output multiple items,
please use the 'Run Once for All Items' mode instead." In per-item mode you return one
bare object — return { json: { … } } — not an array of one.
So: if you're reading "Code doesn't return items properly", you are definitely in Run Once for All Items mode. That's already one thing narrowed down.
n8n appends location information to these messages when it has it:
… [item 4] — it knows which item failed… [line 12] — it knows which line… [line 12, for item 4] — bothSo A 'json' property isn't an object [item 4] means item 4 specifically. Items 0–3
were fine, which usually means the failure is data-dependent — a field that's missing
on some records, not a mistake in the shape of your code.
A 'json' property isn't an object — you returned the right outer shape, but a
json key points at a string, a number or undefined. Description: "In the
returned data, every key named 'json' must point to an object." Same for
A 'binary' property isn't an object.`items` is not defined. Did you mean `$input.all()`? — you're using the old
Function node's variable. n8n adds that suggestion itself, but only when you haven't
declared items yourself. The per-item equivalent is
`item` is not defined. Did you mean `$input.item.json`?Can't use .first() here — described as "This is only available in 'Run Once for
All Items' mode". Thrown for .first(), .last(), .all() and .itemMatching()
used in per-item mode.pairedItem.The Code node sits on a boundary. Inside it you have ordinary JavaScript values; the moment you return, n8n needs items — objects in a fixed envelope that the rest of the workflow can carry, link and display. Nothing can translate an arbitrary JS value into that envelope unambiguously, so n8n validates instead of guessing, and throws at the boundary rather than letting a malformed item travel three nodes and fail somewhere that makes no sense.
The wrapping rule above is the one place n8n does guess — and it only guesses when your object gives it no reason to think you meant something else.
Sources: packages/nodes-base/nodes/Code/result-validation.ts,
ValidationError.ts, JsCodeValidator.ts and reserved-key-found-error.ts in n8n's
repository, read directly. n8n's Code node docs do not list these error strings.
Last verified 15 September 2026 against n8n 2.39.5 (source read on master @ 2.40.0). n8n changes these messages between releases; if the wording you see differs, the version above is what this page was checked against.