Playbook: n8n 2.26.x HTTP Request and IF Node Quirks

Applies to: n8n 2.26.x on bms-4
Discovered: 2026-06-23 during mezmo-alert-router development (issue #1112)
Severity: Workflow-breaking if not understood — silent failures, wrong routing


Quirk 1: specifyBody bug — body never sent or sent as form key

Symptom

HTTP Request node with sendBody: true and a JSON payload receives a 400 or empty body error from the target API. Discord returns: "Cannot send an empty message".

Root cause

  • specifyBody: "keypairs" — sends empty body (no content at all)
  • specifyBody: "string" — wraps the entire JSON string as a form field key with empty value: {"{\"embeds\":[...]}": ""} — the JSON becomes the key name, not the value

Fix

Use specifyBody: "json" and put the JSON string in the jsonBody parameter (not body):

{
  "sendBody": true,
  "bodyContentType": "json",
  "specifyBody": "json",
  "jsonBody": "={{ $json.discord_payload }}"
}

The value of jsonBody must be a JSON string, not a JavaScript object expression. Produce it in a preceding Code node:

const discord_payload = JSON.stringify({
  embeds: [{title: "...", color: 15158332, description: "..."}]
});

Quirk 2: IF node isTrue operator does not route correctly (typeVersion 2.2)

Symptom

IF node with condition $json.my_bool isTrue always routes to the false branch, even when my_bool is true.

Cross-node reference $('PreviousNode').item.json.my_bool returns undefined / empty after a runOnceForAllItems Code node — the pairedItem metadata is missing.

Root cause

The isTrue operator in IF node typeVersion: 2.2 has unreliable behaviour with boolean values. Cross-node references via $('NodeName').item require pairedItem metadata that Code nodes with runOnceForAllItems do not set.

Fix

Use IF node typeVersion: 1 with string equality:

{
  "type": "n8n-nodes-base.if",
  "typeVersion": 1,
  "parameters": {
    "conditions": {
      "string": [
        {
          "value1": "={{ $json.create_gh_issue }}",
          "operation": "equal",
          "value2": "yes"
        }
      ]
    }
  }
}

In the Code node, output "yes" or "no" (strings) instead of true / false (booleans).


Quirk 3: HTTP Request node overwrites $json for all downstream nodes

Symptom

A node downstream of an HTTP Request node sees $json as the HTTP response body (often {} for Discord/GitHub 204 responses), not the data from the original trigger/Code node.

Root cause

In n8n, when nodes are chained linearly, each node receives the output of the node immediately before it. An HTTP Request node’s output is the API response — which replaces whatever was in $json before.

Fix

Use parallel fanout from the Code node — connect it to BOTH the HTTP node and the IF/downstream node simultaneously. In the workflow JSON:

"Prepare Alert": {
  "main": [[
    {"node": "Send Discord", "type": "main", "index": 0},
    {"node": "IF GH Issue",  "type": "main", "index": 0}
  ]]
}

This means the IF node receives data from Prepare Alert directly, not from the Send Discord HTTP response. Both Send Discord and IF GH Issue run in parallel after Prepare Alert completes.

In the n8n UI: drag a connection from the Code node output to the IF node input — you can connect one output port to multiple downstream nodes.


Summary table

QuirkWrong approachCorrect approach
HTTP body not sentspecifyBody: "keypairs" or "string"specifyBody: "json" + jsonBody param with JSON string
IF always routes falseisTrue on boolean, typeVersion 2.2typeVersion 1, string "yes"/"no" equality
Downstream $json is HTTP responseLinear chain: Code → HTTP → IFParallel fanout: Code → [HTTP, IF] simultaneously

  • n8n-workflows/mezmo-alert-router.json — reference implementation using all three fixes
  • docs/n8n-operations.md §mezmo-alert-router — full workflow docs including test commands
  • n8n GitHub: https://github.com/n8n-io/n8n/issues — search for specifyBody and isTrue for upstream tracking