opencode-skills-collection 4.0.7 → 4.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +19 -1
  2. package/bundled-skills/antigravity-maintainer-batch-release/SKILL.md +22 -8
  3. package/bundled-skills/antigravity-maintainer-batch-release/agents/openai.yaml +4 -0
  4. package/bundled-skills/anywrite/SKILL.md +18 -8
  5. package/bundled-skills/cloudflare-security-audit/SKILL.md +5 -3
  6. package/bundled-skills/cloudflare-security-audit/references/RECONNAISSANCE.md +2 -0
  7. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  8. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  9. package/bundled-skills/docs/maintainers/aas-agent-first-control-plane-preview-profile.md +2 -2
  10. package/bundled-skills/docs/maintainers/aas-agent-first-control-plane-v1-worklog.md +1 -1
  11. package/bundled-skills/docs/maintainers/merge-batch.md +3 -3
  12. package/bundled-skills/docs/maintainers/merging-prs.md +13 -17
  13. package/bundled-skills/docs/maintainers/pr-autonomy.md +2 -2
  14. package/bundled-skills/docs/maintainers/release-process.md +17 -2
  15. package/bundled-skills/docs/maintainers/repo-growth-seo.md +3 -3
  16. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  17. package/bundled-skills/docs/users/aas-core.md +38 -3
  18. package/bundled-skills/docs/users/bundles.md +1 -1
  19. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  20. package/bundled-skills/docs/users/faq.md +2 -0
  21. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  22. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  23. package/bundled-skills/docs/users/usage.md +4 -4
  24. package/bundled-skills/docs/users/visual-guide.md +4 -4
  25. package/bundled-skills/docs/vietnamese/README.vi.md +4 -4
  26. package/bundled-skills/hf-cloud-aws-context-discovery/SKILL.md +96 -0
  27. package/bundled-skills/loki-mode/examples/todo-app-generated/backend/package-lock.json +3 -3
  28. package/bundled-skills/markstream-angular/SKILL.md +76 -0
  29. package/bundled-skills/markstream-custom-components/SKILL.md +74 -0
  30. package/bundled-skills/markstream-custom-components/references/patterns.md +14 -0
  31. package/bundled-skills/markstream-install/SKILL.md +3 -1
  32. package/bundled-skills/markstream-migration/SKILL.md +78 -0
  33. package/bundled-skills/markstream-migration/references/adoption-checklist.md +10 -0
  34. package/bundled-skills/markstream-nuxt/SKILL.md +71 -0
  35. package/bundled-skills/markstream-react/SKILL.md +74 -0
  36. package/bundled-skills/markstream-svelte/SKILL.md +68 -0
  37. package/bundled-skills/markstream-vue/SKILL.md +71 -0
  38. package/bundled-skills/markstream-vue2/SKILL.md +70 -0
  39. package/bundled-skills/markstream-vue2-cli/SKILL.md +70 -0
  40. package/bundled-skills/markstream-vue2-vite/SKILL.md +69 -0
  41. package/bundled-skills/n8n-agents/SKILL.md +301 -0
  42. package/bundled-skills/n8n-agents/references/CHAT_AGENT_PATTERNS.md +228 -0
  43. package/bundled-skills/n8n-agents/references/EXAMPLES.md +432 -0
  44. package/bundled-skills/n8n-agents/references/HUMAN_REVIEW.md +180 -0
  45. package/bundled-skills/n8n-agents/references/MEMORY.md +139 -0
  46. package/bundled-skills/n8n-agents/references/RAG.md +102 -0
  47. package/bundled-skills/n8n-agents/references/STRUCTURED_OUTPUT.md +163 -0
  48. package/bundled-skills/n8n-agents/references/SUBWORKFLOW_AS_TOOL.md +199 -0
  49. package/bundled-skills/n8n-agents/references/SYSTEM_PROMPT.md +151 -0
  50. package/bundled-skills/n8n-agents/references/TOOLS.md +199 -0
  51. package/bundled-skills/n8n-binary-and-data/SKILL.md +269 -0
  52. package/bundled-skills/n8n-binary-and-data/references/AGENT_TOOL_BINARY.md +227 -0
  53. package/bundled-skills/n8n-binary-and-data/references/BINARY_BASICS.md +187 -0
  54. package/bundled-skills/n8n-binary-and-data/references/CDN_REQUIREMENT.md +109 -0
  55. package/bundled-skills/n8n-binary-and-data/references/MERGE_FOR_CONTEXT.md +130 -0
  56. package/bundled-skills/n8n-code-tool/SKILL.md +358 -0
  57. package/bundled-skills/n8n-code-tool/references/ERROR_PATTERNS.md +178 -0
  58. package/bundled-skills/n8n-code-tool/references/INPUT_SCHEMA.md +132 -0
  59. package/bundled-skills/n8n-error-handling/SKILL.md +289 -0
  60. package/bundled-skills/n8n-error-handling/references/API_WORKFLOWS.md +256 -0
  61. package/bundled-skills/n8n-error-handling/references/ERROR_WORKFLOWS.md +178 -0
  62. package/bundled-skills/n8n-error-handling/references/NODE_ERROR_OUTPUTS.md +171 -0
  63. package/bundled-skills/n8n-error-handling/references/RESPONSE_SHAPES.md +220 -0
  64. package/bundled-skills/n8n-multi-instance/SKILL.md +208 -0
  65. package/bundled-skills/n8n-node-configuration/references/NODE_FAMILY_GOTCHAS.md +241 -0
  66. package/bundled-skills/n8n-subworkflows/SKILL.md +271 -0
  67. package/bundled-skills/n8n-subworkflows/references/NAMING_AND_DISCOVERY.md +130 -0
  68. package/bundled-skills/n8n-subworkflows/references/SUBWORKFLOW_PATTERNS.md +147 -0
  69. package/bundled-skills/pptx-deck-creation/SKILL.md +2 -0
  70. package/bundled-skills/pptx-deck-creation/references/design-profiles.md +2 -0
  71. package/bundled-skills/sshepherd/SKILL.md +16 -6
  72. package/bundled-skills/using-n8n-mcp-skills/SKILL.md +193 -0
  73. package/bundled-skills/weaviate-cookbooks/references/pdf_multimodal_rag.md +12 -6
  74. package/package.json +1 -1
  75. package/skills_index.json +596 -13
@@ -0,0 +1,208 @@
1
+ ---
2
+ name: n8n-multi-instance
3
+ description: Select, verify, and safely switch n8n MCP instances across production, staging, teams, or clients, especially before credential writes.
4
+ risk: critical
5
+ source: https://github.com/czlonkowski/n8n-skills/tree/main/skills/n8n-multi-instance
6
+ source_repo: czlonkowski/n8n-skills
7
+ source_type: community
8
+ date_added: "2026-07-21"
9
+ author: Romuald Czlonkowski
10
+ license: MIT
11
+ license_source: https://github.com/czlonkowski/n8n-skills/blob/main/LICENSE
12
+ ---
13
+
14
+ # Working with multiple n8n instances over MCP
15
+
16
+ ## When to Use
17
+
18
+ Use this skill whenever one MCP connection can target multiple n8n instances, before instance-specific reads or writes, and whenever results suggest the session is aimed at the wrong environment.
19
+
20
+ Resolve the target by stable instance ID, verify it with a read-only health check, and state the resolved environment before mutations. Require explicit confirmation for credential create/update/delete operations, never print secret values, and stop on ambiguous targeting rather than guessing.
21
+
22
+ When the `n8n_instances` tool is available, the user has **multi-instance mode** on: one MCP
23
+ connection can reach several n8n instances (e.g. `prod`, `staging`, or one per client/team).
24
+ Every other n8n tool (`n8n_get_workflow`, `n8n_list_workflows`, `n8n_update_partial_workflow`,
25
+ `n8n_manage_datatable`, `n8n_manage_credentials`, `n8n_executions`, `n8n_test_workflow`, …) runs
26
+ against **whichever instance this session is currently targeting**. There is no per-call instance
27
+ argument: you change the target only by switching. Target the wrong instance and a read returns the
28
+ wrong data and a write lands in the wrong place — usually with **no error** (the one exception is an
29
+ ambiguous credential write, which fails closed; see below). So target deliberately.
30
+
31
+ If the `n8n_instances` tool is **not** present, the account is single-instance: ignore this skill
32
+ and use the n8n tools directly.
33
+
34
+ ## Golden rules
35
+
36
+ Six rules. Each prevents a class of silent misroute.
37
+
38
+ 1. **Discover first.** Call `n8n_instances({mode:"list"})` before acting so you know the instance
39
+ names and which one is `current`.
40
+ 2. **Switch by name to your target** before doing work on a non-default instance:
41
+ `n8n_instances({mode:"switch", name:"<instance name>"})`. The match is case-insensitive.
42
+ 3. **Switch in its own turn.** Never put a `switch` and a dependent operation in the **same
43
+ parallel tool-call batch**. Calls in one batch have no guaranteed order, so the dependent call
44
+ can be resolved against the *previous* instance before the switch's session state is visible.
45
+ Switch, let it return, *then* operate.
46
+ 4. **Verify before high-stakes ops.** Immediately before creating/updating/deleting **credentials**
47
+ (and before destructive workflow edits), confirm `current` is the instance you intend — primary
48
+ check is `n8n_instances({mode:"list"})`. The system fail-closes only the *ambiguous* credential
49
+ case (rule 6); an explicit switch to the **wrong** instance still writes there silently, so this
50
+ check is on you.
51
+ 5. **An unexpected `NOT_FOUND` is almost always a wrong-instance misroute, not a deletion.** Don't
52
+ recreate the object. Re-check the current instance and retry (see Recovery).
53
+ 6. **On `INSTANCE_AMBIGUOUS`, switch on *this* session, then retry.** The system is refusing to
54
+ write a secret because this session never picked a target itself. Comply — run `switch` here to
55
+ confirm the instance, then retry the write. Don't work around it or retry blindly.
56
+
57
+ ## Core workflow
58
+
59
+ ```
60
+ 1. n8n_instances({mode:"list"}) # see available[] + current + default
61
+ 2. n8n_instances({mode:"switch", name:"prod"}) # bind THIS session to "prod"
62
+ → returns { previous, current }; confirm current.name == "prod"
63
+ 3. (do your work) n8n_list_workflows / n8n_get_workflow / n8n_manage_datatable / ...
64
+ 4. Before a credential write or a delete:
65
+ n8n_instances({mode:"list"}) → re-confirm current, THEN n8n_manage_credentials({action:"create", ...})
66
+ ```
67
+
68
+ To move to another instance, just `switch` again. The whole session follows the switch.
69
+
70
+ ## The `n8n_instances` tool
71
+
72
+ Two modes (`mode` is required and enum-validated):
73
+
74
+ - `{mode:"list"}` → `{ current, default, available }`, no side effects.
75
+ - `current` and `default` are each one instance `{ id, name, url, isDefault }` (or `null`).
76
+ - `available` is every instance, each with an extra `isCurrent` boolean. Match by **`name`**;
77
+ never hard-code `id`.
78
+ - `{mode:"switch", name:"<name>"}` → `{ previous, current }`, and binds this session to the named
79
+ instance. `name` is case-insensitive.
80
+
81
+ ### Error envelope (from the `n8n_instances` tool)
82
+
83
+ Every error returns `{ error: "<CODE>", message, … }`. The ones you'll actually hit:
84
+
85
+ | Code | When | What to do |
86
+ |---|---|---|
87
+ | `UNKNOWN_INSTANCE` | `name` matches no instance | Pick a name from the `available` list in the error payload and retry. |
88
+ | `NAME_REQUIRED` | `switch` with no `name` | Re-call with a `name` (the error lists the valid ones in `available`). |
89
+ | `MULTI_INSTANCE_DISABLED` | multi-instance mode is off | There's nothing to switch; use the n8n tools directly. The user can enable it at the n8n-mcp dashboard. |
90
+ | `NO_SESSION` | the request has **neither** an MCP session id **nor** a credential id | A selection has nowhere to land. Reconnect / initialize a session, then switch. |
91
+ | `UNKNOWN_MODE` | `mode` wasn't `list`/`switch` | Use `list` or `switch`. |
92
+ | `INVALID_CONTEXT` | server-side metadata missing | A server bug, not your input — report it. |
93
+
94
+ > Instance names can never be `default`, `current`, `list`, or `switch` (reserved), so you'll never
95
+ > see an instance literally named after a mode or field.
96
+
97
+ ### `INSTANCE_AMBIGUOUS` (from the credential-write path, not the tool)
98
+
99
+ A separate, higher-stakes error. It is **not** returned by `n8n_instances` — it's returned by the
100
+ server when you call `n8n_manage_credentials` to **create/update/delete** a credential and the target
101
+ instance is ambiguous: this session never switched on its own but inherited a switch made elsewhere
102
+ (a fan-out / reconnect), pointing at a **non-default** instance. Rather than risk writing a secret to
103
+ the wrong instance, the server **blocks the write** (it never reaches n8n, no quota is charged) and
104
+ returns:
105
+
106
+ ```json
107
+ {
108
+ "error": "INSTANCE_AMBIGUOUS",
109
+ "message": "… the session issuing this request never switched there itself … Re-run n8n_instances({mode:\"switch\", name:\"…\"}) on this session to confirm the target …",
110
+ "lastSelected": { "id": "…", "name": "…" },
111
+ "default": { "id": "…", "name": "…" }
112
+ }
113
+ ```
114
+
115
+ **Fix:** decide which instance you actually want (`lastSelected` is the inherited switch, `default`
116
+ is the account default), run `n8n_instances({mode:"switch", name:"…"})` on **this** session, then
117
+ retry the write. See rule 6.
118
+
119
+ ## How targeting behaves (mental model)
120
+
121
+ - A `switch` **binds this session** to the chosen instance. The binding **persists for the rest of
122
+ the session and survives reconnects, idle, and backend deploys** (~24h, the MCP session lifetime)
123
+ — you should not need to re-switch before every call.
124
+ - Other sessions / terminals are **independent**: switching here does not move them.
125
+ - One session targets **one instance at a time**. There is no per-call instance argument; you
126
+ change the target only via `switch`.
127
+ - **Reads and non-credential writes** route to the currently-selected instance, silently — a
128
+ misroute produces wrong data or a `NOT_FOUND`, not an error.
129
+ - **Credential writes are the one guarded case.** They route the same way, except the server
130
+ fail-closes the *ambiguous* state (a session that never switched, recovered onto a non-default
131
+ instance) with `INSTANCE_AMBIGUOUS`. This is a safety net, not a substitute for rule 4: an
132
+ explicit switch to the wrong instance still writes there.
133
+ - **If your selected instance is deleted** (the user removes it mid-session), the next call silently
134
+ falls back to your **default** instance — no error. So default's data appearing where you expected
135
+ another instance's can look like "my data vanished." Re-list to see where you are.
136
+
137
+ ## Recovery playbook
138
+
139
+ | Symptom | What it usually means | Do this |
140
+ |---|---|---|
141
+ | `INSTANCE_AMBIGUOUS` on a credential create/update/delete | This session never switched itself; the system won't guess which instance to write the secret to | Run `n8n_instances({mode:"switch", name:"<target>"})` on this session (the error names `lastSelected` and `default` — pick the one you want), then retry the write. Never retry blindly. |
142
+ | `NOT_FOUND` for a workflow/datatable/credential you **know exists** | You're pointed at the wrong instance — **not** that it was deleted | `n8n_instances({mode:"list"})` → check `current`. If it's not your target, `switch` and retry. **Do not recreate the object.** |
143
+ | A read returns **empty or unfamiliar** data | Wrong-instance read, or a silent fallback to `default` after your instance was deleted | `n8n_instances({mode:"list"})`, confirm `current`, switch if needed, re-read before drawing conclusions. |
144
+ | `UNKNOWN_INSTANCE` on `switch` | The `name` is wrong (typo, or you guessed) | Read the `available` names in the error and switch to one of those. Names are case-insensitive. |
145
+ | `n8n_health_check` reports an `instanceName` you didn't expect | This session is on a different instance than you think | `switch` to the intended instance, then proceed. |
146
+ | Repeated misroutes within one turn | You batched a `switch` with dependent work | Split them: `switch` alone, await the result, then operate one logical step at a time. |
147
+
148
+ After any recovery switch, sanity-check with `n8n_instances({mode:"list"})` (read `current`) as the
149
+ primary signal. `n8n_health_check` also returns the resolved instance under `details.instanceName`,
150
+ but it can be absent on some paths (legacy/chat), so treat it as a secondary confirmation.
151
+
152
+ ## Credential operations (highest stakes)
153
+
154
+ Credentials hold live secrets, and a misrouted credential write puts a secret on the **wrong
155
+ instance**. The server protects the **ambiguous** case automatically — if this session never picked
156
+ a target and inherited a switch to a non-default instance, the write fails closed with
157
+ `INSTANCE_AMBIGUOUS` (rule 6) and never reaches n8n. But that net is narrow: a credential write on a
158
+ session that **did** switch goes through to whatever instance it switched to, with no second
159
+ guess. So:
160
+
161
+ - **Verify `current` immediately before** `n8n_manage_credentials` create/update/delete — call
162
+ `n8n_instances({mode:"list"})` in the same short sequence, not 10 steps earlier where a later
163
+ switch could have moved you.
164
+ - **On `INSTANCE_AMBIGUOUS`**, switch on this session to confirm the target, then retry — don't
165
+ work around it.
166
+ - Credential **reads** (`action:"list"`/`"get"`/`"getSchema"`) are not gated and don't write a
167
+ secret, but a read off the wrong instance returns the wrong schema or list — so still verify
168
+ `current` if the result looks wrong.
169
+ - For the `n8n_manage_credentials` tool itself (CRUD shapes, `getSchema` discovery, never inlining
170
+ secrets into text fields), see `n8n-mcp-tools-expert`.
171
+
172
+ ## Common multi-instance task: copy something between instances
173
+
174
+ To recreate a credential or workflow from instance A on instance B:
175
+
176
+ ```
177
+ 1. switch → A; read the source (n8n_manage_credentials get / n8n_get_workflow)
178
+ 2. switch → B (its own call — never batched with the create below)
179
+ 3. n8n_instances({mode:"list"}) → confirm current == B
180
+ 4. create on B (n8n_manage_credentials create / n8n_create_workflow)
181
+ ```
182
+
183
+ Do each instance's steps in its own turn; never overlap `switch → B` with the create-on-B call
184
+ (rule 3), and switch explicitly on this session before the credential write so it isn't ambiguous
185
+ (rules 4 and 6).
186
+
187
+ ## Quick reference
188
+
189
+ - See instances + where you are: `n8n_instances({mode:"list"})` → `{ current, default, available }`
190
+ - Change target: `n8n_instances({mode:"switch", name:"<name>"})` — its own turn, then operate
191
+ - Confirm target: `current` from `list` (primary); `details.instanceName` from `n8n_health_check` (secondary, may be absent)
192
+ - `UNKNOWN_INSTANCE` → switch to a name from the error's `available` list, then retry
193
+ - `INSTANCE_AMBIGUOUS` (credential write) → `switch` on this session to confirm the target, then retry
194
+ - Unexpected `NOT_FOUND` → verify the instance, switch, retry; **do not recreate**
195
+ - Before credential writes → re-`list`, confirm `current`, then write (the fail-close only covers the ambiguous case)
196
+
197
+ ## Integration with other skills
198
+
199
+ - **n8n-mcp-tools-expert** — owns `n8n_manage_credentials` (CRUD + `getSchema`) and the rule that
200
+ secrets go through the credential system, never text fields. This skill adds the "which instance?"
201
+ layer on top.
202
+ - **using-n8n-mcp-skills** — the router; consult it for which skill owns a given build step.
203
+
204
+ ## Limitations
205
+
206
+ - Instance discovery and switching depend on the connected n8n MCP server exposing multi-instance tools.
207
+ - A successful switch does not authorize mutations or prove that the selected environment is appropriate for the task.
208
+ - Unexpected empty or missing data may have causes other than misrouting; verify before changing targets.
@@ -0,0 +1,241 @@
1
+ # Node Family Gotchas
2
+
3
+ Silent-failure traps grouped by node family. These don't show up in `validate_node` or `validate_workflow` — the workflow validates clean, runs without error, and quietly does the wrong thing. `get_node` shows you the fields exist; it doesn't tell you what happens when you leave them off. This file covers the consequence.
4
+
5
+ Each entry: **symptom** (what you see at runtime), **cause** (why), **fix** (in n8n-mcp / JSON terms).
6
+
7
+ ## Contents
8
+
9
+ - [Switch — dropped items on the unmatched path](#switch)
10
+ - [Merge — wrong input count and the 1-vs-0 index trap](#merge)
11
+ - [Database (Postgres / MySQL / Supabase) — SQL injection, transactions, no-rows](#database)
12
+ - [Slack — Block Kit, threads, operation values](#slack)
13
+ - [Webhook / Respond to Webhook — response codes and modes](#webhook--respond-to-webhook)
14
+ - [Schedule Trigger — timezone, cron fields, missed runs](#schedule-trigger)
15
+
16
+ ---
17
+
18
+ ## Switch
19
+
20
+ **Symptom:** items that match none of the rules vanish. No error, no warning — the workflow just loses data on the unmatched path.
21
+
22
+ **Cause:** without a fallback output, the Switch has nowhere to send unmatched items, so it discards them.
23
+
24
+ **Fix:** set `options.fallbackOutput: "extra"` and give it a name with `options.renameFallbackOutput`. While you're there, name every rule output too — unnamed `0 / 1 / 2` outputs are unreadable a month later, and a failure on "output 2" tells the operator nothing.
25
+
26
+ ```json
27
+ {
28
+ "parameters": {
29
+ "mode": "rules",
30
+ "rules": {
31
+ "values": [
32
+ { "outputKey": "Paid", "renameOutput": true, "conditions": { "...": "..." } },
33
+ { "outputKey": "Refunded", "renameOutput": true, "conditions": { "...": "..." } }
34
+ ]
35
+ },
36
+ "options": {
37
+ "fallbackOutput": "extra",
38
+ "renameFallbackOutput": "Unexpected"
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ Apply surgically with `patchNodeField` on `parameters.options.fallbackOutput`, or with `updateNode` for the full `options` object. After wiring, confirm the fallback branch goes somewhere real (a log, an alert, a NoOp) — an enabled fallback that connects to nothing drops items just the same.
45
+
46
+ ---
47
+
48
+ ## Merge
49
+
50
+ Two traps, both silent. They live on different Merge modes — `numberOfInputs` on Append/Combine, `useDataOfInput` on Choose Branch — so in practice you hit one or the other, not both.
51
+
52
+ ### Trap 1: input count defaults to 2
53
+
54
+ **Symptom:** you wire 3+ sources into a Merge, the canvas shows three wires going in, the workflow validates and runs — but only the first two sources' items appear downstream. The third silently drops.
55
+
56
+ **Cause:** `numberOfInputs` defaults to `2`. The third wire connects to an input slot that doesn't exist on the node.
57
+
58
+ **Fix:** set `numberOfInputs` to match your wire count.
59
+
60
+ ```json
61
+ { "parameters": { "mode": "append", "numberOfInputs": 3 } }
62
+ ```
63
+
64
+ Verify with `get_node` for the merge node on the user's n8n version — the field name has shifted across versions. After building, pull the workflow with `n8n_get_workflow` and confirm `parameters.numberOfInputs` matches the number of source entries in the `connections` object feeding it.
65
+
66
+ ### Trap 2: `useDataOfInput` is 1-indexed, connections are 0-indexed
67
+
68
+ **Symptom:** the Merge passes through the wrong source. Downstream gets real data with real field names — just from the wrong upstream branch. Looks identical to a working flow; the shape is right, the contents are wrong.
69
+
70
+ **Cause:** `parameters.useDataOfInput` matches the UI labels (Input 1, Input 2, Input 3 — **1-indexed**), but the wiring position in `connections.<source>.main[idx]` is **0-indexed** like every other array. Off by one.
71
+
72
+ **Fix — the translation rule:**
73
+
74
+ > `useDataOfInput: "N"` is fed by the connection at `main[N-1]`.
75
+
76
+ | `useDataOfInput` | Connection slot |
77
+ |---|---|
78
+ | `"1"` | `connections.<source>.main[0]` |
79
+ | `"2"` | `connections.<source>.main[1]` |
80
+ | `"3"` | `connections.<source>.main[2]` |
81
+
82
+ When you add the connection via `n8n_update_partial_workflow`, the `addConnection` operation targets a specific input index. To pass through Input 2, the source whose data you want must land on the connection at `main[1]`. After wiring, **verify with `n8n_get_workflow`**: read the `connections` object and confirm the source you intend to pass through actually sits at `main[N-1]`. This is the only reliable check — it won't surface in validation.
83
+
84
+ ---
85
+
86
+ ## Database
87
+
88
+ Covers Postgres, MySQL, and Supabase (when used via the Postgres node against the same database). The exact field set differs per node and version — `get_node` is canonical. This is the security and behavior layer it doesn't show.
89
+
90
+ ### Never interpolate user input into SQL
91
+
92
+ **Symptom:** the query works in testing, then a value containing a quote or `;` produces a SQL error — or worse, executes injected SQL. `$json.email = "x'; DROP TABLE users; --"` is game over.
93
+
94
+ **Cause:** n8n substitutes `{{ ... }}` expressions into the query text **before** the database driver binds parameters. Anything inside `{{ }}` becomes part of the SQL itself, not a bound value.
95
+
96
+ **Fix:** use `$1, $2, ...` placeholders in the query and pass values through `options.queryReplacement`. The values flow through the driver's parameter binding and never touch the SQL text. (The n8n MySQL node also uses `$1, $2` + `queryReplacement`, not MySQL's native `?` — the node normalizes to the driver.)
97
+
98
+ ```json
99
+ {
100
+ "parameters": {
101
+ "operation": "executeQuery",
102
+ "query": "SELECT * FROM users WHERE email = $1",
103
+ "options": {
104
+ "queryReplacement": "={{ $json.email }}"
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ `queryReplacement` takes a comma-separated list — each piece becomes one parameter: `={{ $json.email }},={{ $json.id }}` → `$1, $2`. The `=` prefix is just n8n's expression-mode marker. Treat any DB node with a `{{ ... }}` expression inside `parameters.query` as a critical injection finding.
111
+
112
+ ### Transactions are bounded to one node
113
+
114
+ **Symptom:** two separate DB nodes, the second fails, and the first's write is already committed — no rollback.
115
+
116
+ **Cause:** there is no cross-node transaction in n8n. Atomicity is bounded to a single `executeQuery` invocation.
117
+
118
+ **Fix:** for atomic multi-step writes, put all the statements in one Postgres/MySQL `executeQuery` node and set `options.queryBatching: "transaction"` explicitly — don't rely on the default, which has shifted across node versions (single-query and independent batching are the other modes; confirm the current set and default with `get_node`). Everything that node runs in that execution goes through one BEGIN/COMMIT; any failure rolls it all back. Pre-compute lookups and derived values upstream so the transactional node receives ready-to-write data.
119
+
120
+ ```json
121
+ {
122
+ "parameters": {
123
+ "operation": "executeQuery",
124
+ "query": "INSERT INTO orders (customer_id, total) VALUES ($1, $2)",
125
+ "options": {
126
+ "queryBatching": "transaction",
127
+ "queryReplacement": "={{ $json.customerId }},={{ $json.total }}"
128
+ }
129
+ }
130
+ }
131
+ ```
132
+
133
+ Supabase's REST layer has no transactions — drop to the Postgres node connected directly to the same database when you need atomicity.
134
+
135
+ ### "No rows" produces no items
136
+
137
+ **Symptom:** a `select` / `executeQuery` that matches nothing returns zero items, and the downstream node simply doesn't run — looks like the branch was skipped.
138
+
139
+ **Cause:** zero matched rows = zero n8n output items, and most nodes treat "no input items" as "nothing to do."
140
+
141
+ **Fix:** set `alwaysOutputData: true` on the DB node so a single empty item flows through, then branch on the result with an IF. (This is the same gotcha as write operations — INSERT/UPDATE/DELETE often return 0 items too; `alwaysOutputData: true` keeps the chain alive.)
142
+
143
+ ---
144
+
145
+ ## Slack
146
+
147
+ The exact param shapes shift across versions — `get_node` for `nodes-base.slack` is canonical. These are the traps it won't warn you about.
148
+
149
+ ### Block Kit must be wrapped, or it posts as plain text
150
+
151
+ **Symptom:** you pass a Block Kit array, the request succeeds, but the message arrives as plain text (or empty). No node error, no validation warning.
152
+
153
+ **Cause:** the node accepts a bare array silently and drops the rich content. Slack's `chat.postMessage` expects `{ "blocks": [...] }` — an object with a `blocks` key — and the node forwards your value as-is.
154
+
155
+ **Fix:** wrap the array in an object, in expression mode so the node receives a real object (not a stringified one). Reference the source by node name, not `$json`:
156
+
157
+ ```
158
+ ={{ { "blocks": $('Build Message').item.json.blocks } }}
159
+ ```
160
+
161
+ Don't stringify-then-reparse hybrids (`{{ ... .toJsonString() }}` glued into a string) — they work on some versions but break on escaping and large payloads. Hand the node the structure directly.
162
+
163
+ ### Thread replies need `thread_ts`
164
+
165
+ **Symptom:** a "reply" posts as a new top-level channel message instead of in the thread.
166
+
167
+ **Cause:** without `thread_ts` (the timestamp of the message being replied to), Slack has no thread to attach to.
168
+
169
+ **Fix:** set `thread_ts` to the parent message's `ts`. Use `get_node` to find where the field sits on the current version — it moved out of `otherOptions` where older docs put it. Add `reply_broadcast: true` if the reply should also show in the main channel.
170
+
171
+ ### Operation display name ≠ internal value
172
+
173
+ **Symptom:** you set `operation: "send"` (matching the UI's "Send a message") and validation rejects it.
174
+
175
+ **Cause:** the display label and the stored value diverge. "Send a message" is `operation: "post"`, not `"send"`.
176
+
177
+ **Fix:** read the real operation values from `get_node` for `nodes-base.slack` rather than guessing from the UI label. This display-vs-value mismatch recurs across resource nodes (e.g. "Get Many" → `getAll` on Gmail/Supabase).
178
+
179
+ ---
180
+
181
+ ## Webhook / Respond to Webhook
182
+
183
+ Entry and exit of request/response API workflows. `get_node` is canonical for field shapes; this is the runtime behavior it doesn't show.
184
+
185
+ ### Response code defaults to 200 — even on error branches
186
+
187
+ **Symptom:** an error branch returns HTTP 200 with an error body. The caller's HTTP client sees success while the body says failure — the worst of both worlds, because the caller's error handling never fires.
188
+
189
+ **Cause:** `responseCode` defaults to `200` on every Respond to Webhook node, including the ones you wired to error paths.
190
+
191
+ **Fix:** set `responseCode` explicitly on every Respond branch — 4xx for caller errors (400 validation, 401/403 auth, 409 conflict, 429 rate limit), 5xx for server errors. A workflow can have multiple Respond nodes, one per response shape; n8n returns whichever fires first.
192
+
193
+ ### Use `responseMode: "responseNode"` for real request/response APIs
194
+
195
+ **Symptom:** the caller gets an immediate 200 and never sees the workflow's actual output, even though the workflow computes a response.
196
+
197
+ **Cause:** the Webhook trigger's `responseMode` defaults to `onReceived` (acknowledge immediately, run async). The caller can't see downstream results.
198
+
199
+ **Fix:** set `parameters.responseMode: "responseNode"` on the Webhook trigger and control the response with explicit Respond to Webhook nodes. (`lastNode` returns the last node's output synchronously — fine for simple cases; `responseNode` is the flexible choice for multi-status APIs.)
200
+
201
+ ### `respondWith: "json"` takes the object, not a stringified string
202
+
203
+ **Symptom:** the response body comes back double-encoded — escaped quotes, a JSON string wrapped in another JSON string.
204
+
205
+ **Cause:** the `responseBody` field accepts both an object and a string. If you pass `JSON.stringify(obj)`, n8n serializes that string again.
206
+
207
+ **Fix:** pass the object directly in expression mode and let the node serialize it once:
208
+
209
+ ```
210
+ ={{ { "status": "ok", "id": $('Create Record').item.json.id } }}
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Schedule Trigger
216
+
217
+ `get_node` for `nodes-base.scheduleTrigger` shows the rule structure. These are the behaviors outside the type def.
218
+
219
+ ### Timezone is workflow-level, not per-rule
220
+
221
+ **Symptom:** a job that should fire at 9am local drifts after a DST change or an instance move.
222
+
223
+ **Cause:** the Schedule Trigger uses the **workflow's** timezone (Workflow Settings → Timezone). There is no `timezone` field inside a rule. Without an explicit workflow timezone, it follows the host's clock.
224
+
225
+ **Fix:** set the workflow timezone explicitly for any schedule that must run at a specific local time. The per-rule config has no timezone to set — don't look for one.
226
+
227
+ ### Cron accepts 5 or 6 fields
228
+
229
+ **Symptom:** confusion over whether a cron expression needs a seconds field — the UI hint shows 6 fields, the placeholder shows 5.
230
+
231
+ **Cause:** n8n's cron supports both 5-field (`Minute Hour DoM Month DoW`) and 6-field (`Second Minute Hour DoM Month DoW`) formats. Both are valid.
232
+
233
+ **Fix:** use whichever you intend; just be consistent. For simple recurrences ("every Monday 9am"), the interval modes (`field: "weeks"` etc.) are clearer and less error-prone than cron.
234
+
235
+ ### Restarts can miss runs — design for idempotency
236
+
237
+ **Symptom:** an instance restart or downtime window overlapping a scheduled time, and that run never happens.
238
+
239
+ **Cause:** schedules fire against the instance's clock. If the instance is down at fire time, the run is simply skipped — there's no catch-up queue.
240
+
241
+ **Fix:** for business-critical schedules, make the workflow idempotent (running it twice produces the same result) and, where it matters, detect missed runs at workflow start by comparing the last successful run to the expected cadence and catching up.