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
| Quirk | Wrong approach | Correct approach |
|---|---|---|
| HTTP body not sent | specifyBody: "keypairs" or "string" | specifyBody: "json" + jsonBody param with JSON string |
| IF always routes false | isTrue on boolean, typeVersion 2.2 | typeVersion 1, string "yes"/"no" equality |
Downstream $json is HTTP response | Linear chain: Code → HTTP → IF | Parallel fanout: Code → [HTTP, IF] simultaneously |
Related
n8n-workflows/mezmo-alert-router.json— reference implementation using all three fixesdocs/n8n-operations.md §mezmo-alert-router— full workflow docs including test commands- n8n GitHub: https://github.com/n8n-io/n8n/issues — search for
specifyBodyandisTruefor upstream tracking