N8n Gotchas: Postgres queryParams, Ollama double encoding, Nextcloud Talk auth, and data loss after INSERT

After building an email processing workflow with Ollama, Postgres and Nextcloud Talk,
I ran into four non-obvious bugs. Sharing the fixes here in case others hit the same issues.


BUG-01: Postgres executeQuery – $1/$2 queryParams not working

Symptom: Workflow runs through without error, but nothing is inserted into the database.

Cause: The Postgres node v2.5 does not reliably pass queryParams ($1, $2, …)
in executeQuery mode.

Fix: Add a Code node before the Postgres node to build the complete SQL string:

function esc(v) {
if (v === null || v === undefined) return ‘NULL’;
return “'” + String(v).replace(/\/g, ‘\\’).replace(/‘/g, “’‘”) + “’”;
}

const d = $input.first().json;
const sql = INSERT INTO table (col1, col2) VALUES (${esc(d.val1)}, ${esc(d.val2)});
return [{ json: { …d, sqlQuery: sql } }];

In the Postgres node, use only:
query: ={{ $json.sqlQuery }}

Do not use queryParams at all.


BUG-02: Ollama HTTP Request – double encoded JSON body

Symptom: Ollama returns an error or nonsense output.

Cause: When using specifyBody: “json”, n8n already serializes the object.
Adding JSON.stringify() on top causes double encoding.

Wrong:
jsonBody: ={{ JSON.stringify($json.ollamaPayload) }}

Right:
jsonBody: ={{ $json.ollamaPayload }}


BUG-03: Nextcloud Talk HTTP Request – auth failure (401)

Symptom: 401 Unauthorized when posting to Nextcloud Talk API.

Cause: predefinedCredentialType with typeVersion 4.2 does not work reliably
with external HTTPS endpoints using BasicAuth.

Fix: Use these settings in the HTTP Request node:
typeVersion: 4.4
authentication: genericCredentialType
genericAuthType: httpBasicAuth

Also send the message as a query parameter, not as form body:
sendQuery: true
queryParameters: [{ name: “message”, value: “={{ $json.message }}” }]

And add this header:
OCS-APIRequest: true


BUG-04: Data lost after Postgres INSERT (undefined in next node)

Symptom: The node after a Postgres INSERT receives empty data –
variables show as “undefined”.

Cause: After a write operation, the Postgres node does not pass through
the input data. $json is empty in the next node.

Fix: In the node after the INSERT, reference an earlier node directly:

const d = $(‘Code: Build SQL’).first().json;
// instead of $json.fieldname
// use d.fieldname

Rule of thumb: After any write database operation, always reference
an earlier node – never use $json.

Love this format – sharing mine from recent builds.

BUG-05: AI Agent node silently drops array values in tool calls (MCP Client)

Symptom: Tool executes, no error thrown, but the receiving server rejects the request with something like “Expected JsonArray but got JsonLiteral.”

Cause: The MCP Client node serializes array values as JSON strings before passing them to tools. So ["item1", "item2"] becomes "[\"item1\", \"item2\"]" by the time it hits your tool.

Fix: Either (a) swap MCP Client for a Code Tool node where you manually construct and send the HTTP request, giving you full control over serialization, or (b) add explicit instructions in your tool description like “seedUrls must be a native JSON array, NOT a stringified array” – models are very literal and this alone often fixes it.

BUG-06: HTTP Request node credentials work in Execute Step but 401 in full workflow run

Symptom: Test a single node, credentials work fine. Run the whole workflow, same node returns 401 Unauthorized.

Cause: n8n resolves credentials differently in isolated node test vs full workflow execution. Credentials get resolved at workflow initialization, and a stale or partially cached credential record can decrypt to empty/wrong during a full run while still working fine in isolation.

Fix: Delete the credential and recreate it from scratch. This resets the internal state in the n8n database. After recreating, re-assign it to the node. Switching from predefined credential type to Generic > Bearer Auth with a hardcoded token is a fast workaround to confirm whether it’s a credential resolution issue vs an actual API problem.

These both cost me embarrassing amounts of debugging time before I figured out what was actually happening.