Debug failed or wrong-output workflow executions using executions tools. Load when the user reports execution failures, unexpected node output, empty parameter values after a successful run, or a node showing a red or failed expression error. Also load when a live run you started for the user fails with an error from a node or an external service (for example a 401), before you explain the cause to the user.
92%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Use this skill when debugging workflow execution failures or successful runs with wrong or empty values.
Re-run the failing path with run_workflow (or
workflow_builder_verify_built_workflow) and inspect the real result before responding. Do not
restate that the workflow is "fixed", "verified", or "working", and do not
attribute the reported failure to a test-harness artifact, stale state, or "it
works in production" without a re-run against the failing path. Treat live
signals as real: an execution error, partial coverage (nodesNotReached), an
empty node, or a missing node is a real defect to investigate, not something to
explain away. If you genuinely cannot re-run the failing path, say so plainly and
name what is unconfirmed instead of repeating a success claim.
Say what you checked before you say why it failed. Do not lead with a cause that you did not check. A plausible guess presented first reads as a finding, and the user acts on it.
Check what you can see on the n8n side first. These checks are read-only, so run them unprompted:
list_credentials, with ids for a specific credential),
that the node uses it (the node's credentials in
get_workflow; resolved parameters show only the
auth type, not which credential is bound), and what
test_credential reports. A generic credential,
such as Header Auth, can pass its test without a call to the service, so a
pass does not show that the service accepts it. Say so when you report the
check.debug_execution and the
resolved parameters of the failed node.Then report the checks and their results, and only after that give a cause. You cannot read a stored secret, so a cause about its format or value is a hypothesis: label it as one, and say which check would confirm it. Do not blame the user's account, billing, credit, plan, or quota unless the error says so.
A cause outside the workflow does not mean that the workflow is correct. Before you say that the workflow is wired or configured correctly, read the expression parameters of every node on the failing path, not only the node that failed. An expression that reads the wrong field, or a literal placeholder where an expression belongs, is a defect even when a different error stopped the run. Report each defect separately from the cause of the failed run.
A published workflow runs the version that was published, not the draft you edit. Your save creates a draft, and the draft is not live until somebody publishes it. So a fix to a live workflow changes nothing in production on its own.
Two consequences when the user reports a live workflow failing:
list_executions( workflowId)
returns workflowVersionId on every row, plus workflow.activeVersionId
(the published version) and workflow.draftVersionId. Only a row whose
workflowVersionId equals workflow.activeVersionId ran the published code.
A run of a draft proves nothing about production. A workflow.draftVersionId
different from workflow.activeVersionId means the latest changes, including
any fix you just made, are not live.Do not report a fix as live until the published version is the fixed one. Ask whether to publish it instead. After a publish, a new live run is what confirms the fix — an earlier execution ran the old version.
Use run_workflow with inputData matching the trigger's output
shape — do not rebuild the workflow with a Manual Trigger. For trigger
inputData shapes, read the workspace file
knowledge-base/reference/trigger-input-data-shapes.md
when a sandbox workspace is available.
A message about a failed run often names its execution, for example "currently
viewing its execution <id>". Read that execution by its id. Do not pick a run
from list instead: list can return an older run, and it does not show a
manual run that n8n did not save.
When saving manual executions is turned off, n8n can keep a finished manual run for a short time, only for the user who ran it. You act as that user, so you can read it by its id in that time. When the id is not found, tell the user that you cannot read that run. Ask the user to run it again, or offer to run it yourself: n8n saves every run you start. A new run repeats what its nodes do, so check for write nodes first (see below).
debug_execution already includes failedNode.resolvedParameters —
start there. That bundle has parameters (raw, with expressions intact),
resolved (substituted), failedExpressions (those that threw), and
emptyResolutions (those that resolved to null/undefined/"" silently).
The offending expression is usually visible without a follow-up call. Entries in
either list tagged with reason: "unreconstructable-context" are NOT real bugs —
they reference variables we don't reconstruct in replay ($vars, $secrets,
$response, $request, $pageCount, $ai). The value existed at execution
time; we just don't have it here.
debug_execution tells you what the node received. It does not tell
you whether your fix works. To learn that, run the node itself:
run_workflow_step(workflowId, nodeName, reuseExecutionId=<the failed execution>)reuseExecutionId replays the data the node really received and re-runs only
that node, so the fix meets the same input that broke it. This is the right
first move whenever the user is debugging a read node that already failed a
real execution: the node ran for real once already, and a mock-only check is
what sends the user back for a second session.
The tool refuses a replay that would re-run a node above the target: an unpinned one the execution never reached, one that failed there, or a loop that did not finish. Pick another execution, or tell the user which node would run again.
A step run is a real run. The node uses the user's real credentials and reaches
the user's real systems, on the user's real data. Check what the node does
before you reach for run_workflow_step:
get, getAll, search, list, download, a GET
HTTP Request), or a transform that touches nothing outside the workflow (Set,
IF, Filter, Code without network or filesystem access). Run these.create, update, upsert, delete,
send, append, a non-GET HTTP Request). Running one sends the message,
charges the card, or deletes the row — again, and for real. The user asked
you to debug the node, not to perform its effect.For a write node, debug without running it: read the failed execution with
debug_execution, inspect the resolved parameters with
get_resolved_node_parameters, and explain the fix. That is usually enough,
because a write node's failures are nearly always in its input or its
parameters, both of which you can see without sending anything.
If you genuinely cannot resolve it without a real run, say plainly what the node will do to the user's data, and let the user choose. The approval prompt alone is not consent: the user sees a node name, not "this posts to your #general channel".
"It already ran anyway" is not a reason. It holds only for a node that errored outright and changed nothing. A node that partly succeeded before it failed — a send that delivered some messages and then hit a rate limit — will deliver them again.
mockInputmockInput runs the node on items you supply and skips everything above it.
This is a good way to study one node by itself, and a normal thing to do while
debugging:
Reach for it whenever the question is about the node. Use reuseExecutionId or
a chain run when the question is about the workflow.
Keep the claim at the level of the evidence. A mocked run shows the node
handles the input you gave it; it shows nothing about what the chain really
produces. The result carries inputMode: "mocked" and a mockedNodeNames
list — report the node's behaviour, not the workflow's.
Mocked input does not make a write node safe. The node still runs for real against the user's systems; only its input is invented, which makes the effect less predictable, not more.
A tool never runs on its own. n8n runs it through the node that owns it — usually the Agent. A step run on a tool therefore behaves like a step run on that Agent:
mockInput feeds the Agent's input, not the tool's arguments.reuseExecutionId replays the nodes above the Agent.ranThroughNodeNames lists every node that can run the tool, not the one
that ran it. A tool on several agents lists them all: the engine picks one,
and it does not report which. Do not name a single Agent to the user when
this field holds more than one.get_execution_node_output.The tool's own arguments come from toolArguments — the values the agent would
normally decide:
run_workflow_step(workflowId, nodeName="Search Tickets Tool",
reuseExecutionId=<the failed execution>,
toolArguments={"query": "login fails", "status": "open"})The example targets a read tool on purpose. The write rule above holds here
too, and a tool hides the write behind a friendly name: a step run on a "Create
Ticket" tool creates the ticket again, and toolArguments does not change that.
Only run one when the user has accepted a second write.
Use the argument names from the node's $fromAI calls, which
get_workflow_as_code shows. Pass a plain string instead for a tool
that takes one free-text input (Wikipedia, Code Tool, a vector store used as a
tool).
A tool that declares $fromAI arguments is refused without them: it would
otherwise fail for a reason that has nothing to do with the user's problem, and
you would report that as the defect. A node that holds several tools is refused
outright, because nothing here can name one of its tools the way the agent
does: the "MCP Client Tool" node, and every node the MCP registry added, whose
type is @n8n/mcp-registry.<server slug>. Run the Agent for those, and read
the node's output from that execution.
A sub-node that is not a tool — a model, memory, embeddings — cannot be run this
way at all. Run the Agent, and read the sub-node with
get_execution_node_output on that execution: n8n records every
call a sub-node made while the Agent ran.
When debug_execution doesn't apply because nothing errored, call
get_resolved_node_parameters(executionId, nodeName) on the
node whose output looks off and on the nodes that feed it —
do this unprompted, don't ask the user for permission first. It's a cheap
read-only inspection and the only reliable way to confirm whether an empty value came
from an expression silently resolving to nullish. Check emptyResolutions first; most "this parameter is empty" cases are
expressions resolving to null/undefined/"", not thrown errors.
n8n-io/n8n
c44bd78
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.