n8n-errors-expressions — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited n8n-errors-expressions (Agent Skill) and scored it 45/100 (orange). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 1 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
A base64 string of 128+ characters appears in a documentation file. Encoded prompt injection hides the hostile instruction in base64 — invisible to keyword filters — and relies on the agent's ability to decode it at runtime. There is no normal authoring reason to embed a multi-hundred-byte base64 blob in skill docs.
*.sig, SIGNATURES) outside the documentation.Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Diagnose and fix expression evaluation failures in n8n v1.x workflows. For error type details see references/methods.md. For before/after fixes see references/examples.md. For anti-patterns see references/anti-patterns.md.
| Symptom | Cause | Fix |
|---|---|---|
undefined when accessing $json.field | Field does not exist on current item | Check field name spelling; use $json?.field or $ifEmpty($json.field, fallback) |
TypeError: Cannot read properties of undefined | Accessing nested field on null/undefined parent | Chain optional access: $json.parent?.child?.value |
$itemIndex is not defined in Code node | $itemIndex is NOT available in Code node | Use items.indexOf(item) or loop index variable instead |
$secrets is not defined in Code node | $secrets is NOT available in Code node | Use $env.SECRET_NAME or pass secret via preceding Set node |
$response is not defined | $response used outside HTTP Request node | ONLY use $response in HTTP Request node parameter fields |
Paired item not found | Item linking broken between nodes | Use $("<Node>").first() or $("<Node>").all()[index] instead of .item |
| JMESPath returns unexpected result | Parameter order swapped | ALWAYS use $jmespath(object, searchString) — object first, search second |
| Expression returns empty string | Field exists but value is null, "", or undefined | Use $ifEmpty($json.field, "default") to provide fallback |
$getWorkflowStaticData returns empty | Static data not available during manual test | ALWAYS test static data with active workflow (webhook/trigger), NEVER manual execution |
TypeError: X is not a function | Calling n8n extension method on wrong type | Verify data type: .extractEmail() requires string, .average() requires array |
$("<Node>").item returns wrong data | Expression evaluated in non-matching context | Use $("<Node>").itemMatching(index) in Code node; use .first() or .all() for explicit access |
Python AttributeError on item access | Using dot notation in Python Code node | ALWAYS use bracket notation: item["json"]["field"] |
$pageCount is not defined | Used outside HTTP Request node pagination | $pageCount is ONLY available in HTTP Request node |
| Number treated as string in comparison | n8n expression returned string type | Explicitly convert: Number($json.price) or use parseInt()/parseFloat() |
| Date comparison fails | Comparing string dates instead of DateTime | Convert with .toDateTime() then compare with .diffTo() or .isBetween() |
Use this matrix to determine which variables are available in each context.
| Variable | Expression Fields | Code Node (JS) | Code Node (Python) | HTTP Request Node |
|---|---|---|---|---|
$json / $binary | YES | YES | _json / _binary | YES |
$input.item | YES | YES (each-item mode) | _item | YES |
$input.all() | YES | YES | _items | YES |
$("<Node>").item | YES | NO (use .itemMatching()) | NO | YES |
$("<Node>").itemMatching() | YES | YES | YES (_("<Node>")) | YES |
$itemIndex | YES | NO | NO | YES |
$runIndex | YES | YES | YES | YES |
$secrets | YES | NO | NO | YES |
$env | YES | YES | _env | YES |
$vars | YES | YES | _vars | YES |
$now / $today | YES | YES | YES | YES |
$execution | YES | YES | _execution | YES |
$workflow | YES | YES | _workflow | YES |
$prevNode | YES | YES | YES | YES |
$response | NO | NO | NO | YES |
$pageCount | NO | NO | NO | YES |
$parameter | YES | YES | YES | YES |
$ifEmpty() | YES | YES | YES | YES |
$jmespath() | YES | YES | _jmespath() | YES |
$getWorkflowStaticData() | YES | YES | _getWorkflowStaticData() | YES |
$execution.customData | YES | YES | _execution | YES |
Expression returns unexpected result
|
+-- Is the variable available in this context?
| +-- NO --> Check Variable Availability Matrix above
| +-- YES --> Continue
|
+-- Does the field exist on the item?
| +-- Check with: {{ Object.keys($json) }}
| +-- Field missing --> Fix field name or check upstream node output
| +-- Field exists --> Continue
|
+-- Is the value null/undefined/empty?
| +-- YES --> Use $ifEmpty($json.field, fallback)
| +-- NO --> Continue
|
+-- Is the type correct?
| +-- String where number expected --> Number($json.field)
| +-- Number where string expected --> String($json.field)
| +-- String where date expected --> $json.field.toDateTime()
| +-- Type is correct --> Continue
|
+-- Is item linking the problem?
| +-- "Paired item not found" error --> See Paired Item Errors below
| +-- Wrong item data --> Use explicit .first()/.all() instead of .item
| +-- Correct linking --> Check expression syntaxWhen you see "Paired item not found" or get wrong data from $("<Node>").item:
$("<Node>").first() — ALWAYS returns first item (safe fallback)$("<Node>").all()[index] — Access by position$("<Node>").itemMatching(currentIndex) — Trace back from current item (preferred in Code node)$("<Node>").item. ALWAYS use $("<Node>").itemMatching(index).n8n uses $jmespath(object, searchString) — this is the OPPOSITE of the JMESPath spec's search(searchString, object).
// CORRECT — object first, search string second
{{ $jmespath($json.data, "[*].name") }}
// WRONG — search string first (JMESPath spec order)
{{ $jmespath("[*].name", $json.data) }}ALWAYS verify: first argument is the data object, second argument is the query string.
$getWorkflowStaticData() has specific constraints:
{}| Error Message | Meaning | Resolution |
|---|---|---|
Expression evaluation error | Generic expression parse failure | Check syntax: matching {{ }}, valid JS |
Cannot read properties of undefined (reading 'X') | Accessing property on null/undefined | Add null checks: $json.parent?.child |
X is not a function | Wrong method for data type | Check type: strings have .extractEmail(), arrays have .average() |
Paired item information is missing | Item link chain broken | Use .first(), .all(), or .itemMatching() |
ReferenceError: $itemIndex is not defined | Used in Code node | Use loop index or items.indexOf(item) |
ReferenceError: $secrets is not defined | Used in Code node | Use $env or pass via Set node |
Invalid left-hand side in assignment | Assignment = inside expression | Expressions are read-only; use Code node for assignment |
Unexpected token | Syntax error in expression | Check for unmatched brackets, quotes, or template literals |
ALWAYS remember these restrictions when writing Code node logic:
$itemIndex — NOT available. Use loop index or items.indexOf(item).$secrets — NOT available. Use $env.SECRET_NAME instead.$("<Node>").item — NOT recommended. Use $("<Node>").itemMatching(index).item["json"]["field"], NEVER dot notation.$response is ONLY available in HTTP Request node parameter fields:
| Property | Returns | Context |
|---|---|---|
$response.body | Response body object | HTTP Request node ONLY |
$response.headers | Response headers | HTTP Request node ONLY |
$response.statusCode | HTTP status code | HTTP Request node ONLY |
$response.statusMessage | Status message | HTTP Request node ONLY |
If you need HTTP response data in other nodes, the HTTP Request node automatically outputs the response as $json for downstream nodes.
ALWAYS handle potentially missing data with one of these approaches:
$json.parent?.child?.value for nested access{{ $json.field ? $json.field : "default" }} for conditional logic {{ (function() {
const val = $json.field;
if (val === null || val === undefined) return "N/A";
return val.toString();
})() }}| From | To | Method |
|---|---|---|
| String | Number | Number($json.field) or parseInt() / parseFloat() |
| String | Boolean | $json.field.toBoolean() |
| String | DateTime | $json.field.toDateTime() |
| Number | String | String($json.field) or $json.field.format() |
| Number | Boolean | $json.field.toBoolean() (0 = false, else true) |
| Number | DateTime | $json.field.toDateTime() (Unix ms or seconds) |
| Boolean | Number | $json.field.toNumber() (true=1, false=0) |
| Boolean | String | $json.field.toString() |
| Array | String | $json.arr.toJsonString() |
| Object | String | $json.obj.toJsonString() |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.