Skip to main content

Context

Three separate type coercion bugs surfaced during TOMMY pipeline debugging, each requiring a different fix pattern. All three share the same root cause: n8n’s expression engine silently converts types before nodes evaluate them.

The Three Gotchas

1. Array “is not empty” with null default

Symptom: If node crashes with “Wrong type: ” is a string but was expecting an array”. Cause: Code node defaults forge_tasks to null. The If node condition {{ $json.forge_tasks }} with typeValidation: "strict" and operator “is not empty” (array) receives an empty string (n8n coerces null → ''). Fix: Default to [] in the Code node AND change the If node condition to {{ $json.forge_tasks.length }} with is greater than 0 (number comparison). This sidesteps array type detection entirely.

2. Code node return shape in runOnceForEachItem

Symptom: “Code doesn’t return a single object [item 0]”. Cause: Code node in runOnceForEachItem mode returns [{json: {...}}] (array-wrapped). This mode expects a bare {json: {...}} — the array wrapper is only valid in runOnceForAllItems mode. Fix: Remove the [ and ] around the return object.

3. Merge node “empty” output drops all data

Symptom: Downstream Code node receives {} — all context fields (repo, branch, dry_run) are undefined. Cause: Merge node configured with mode: "chooseBranch" and output: "empty". This signals which branch completed but passes no data. Downstream nodes that expect $input.first().json to contain context get nothing. Fix: Have downstream Code nodes use $('UpstreamNodeName').first().json to reference data directly instead of relying on $input.

Business Takeaway

n8n’s type system is implicit and underdocumented. Each of these three bugs produced a different error message for the same category of problem (type mismatch). Building a reference table of symptom → root cause → fix (as we’ve done in the debugging skill) prevents re-discovery cost on future workflows.