Read the log, find the one that broke
One real execution log, one broken reference, and the debugging order that finds it in four steps.
Everything you have built so far works. This lesson is about the day it does not — and specifically about the failure that is worst to debug, which is not the one that crashes loudly. It is the one where a node reports success and the row it should have written is not there.
You are going to read one real execution log, find one broken reference, and fix it. The log is genuine, from this platform, and the bug is one that survived weeks of testing.
The symptom
A workflow ran. Seven nodes green. The eighth — the one that writes an audit note to the customer's record — is red:
CRM API Error (500): {'error': 'validation_error', 'field': 'system', 'message': 'An unexpected error occurred while creating the note', 'code': 'INTERNAL_ERROR'}
Which tells you almost nothing. field: system and INTERNAL_ERROR are the API saying "something you sent was unusable and I could not say which part". The temptation here is to go and read the note-creation code. Do not. Read what the node was actually handed.
Where the answer is
Open the failing node in the execution log and look at its resolved config — what the templates became, not what you typed:
{"content": "RESOLUTION EXECUTED. Reviewer score 5/5. Human decision: Approve.", "entity_type": "deal", "entity_id": ""}
There it is. entity_id: "". The content resolved perfectly — the score, the human's decision, all of it. One field came out empty, and the API's unhelpful 500 is what an empty foreign key looks like from the other side.
The node was configured with this:
{{account_lookup.results[0].id}}
Which looks entirely reasonable. There is a node called account_lookup. It does return results. And the log carries one more line that explains everything:
template_variables_detected: []
Zero. The reference was not resolved and found empty — it was never recognised as a reference at all. That distinction is the entire diagnosis.
Why it could never have worked
Compare the node list the execution actually produced against the canvas you drew:
| On the canvas | In the execution log |
|---|---|
| trigger, attempts, proposer, reviewer, verdict, gate, approval, audit | all present ✓ |
account_lookup | absent |
policy_lookup | absent |
trigger, attempts, proposer, reviewer, verdict, gate, approval, audit
- In the execution log
- all present ✓
account_lookup
- In the execution log
- absent
policy_lookup
- In the execution log
- absent
Both missing nodes are tools — attached to an agent's tool handle rather than wired into the flow. A tool runs inside the agent that owns it, and its output lands in that agent's tool-call record. It is never a top-level step, so {{account_lookup.…}} has nothing to point at. It resolves to nothing, quietly, and the workflow carries on.
The rule to carry away. You can only reference a node that appears in the execution log as its own step. If it is a tool, its results belong to the agent, not to the workflow.
The fix, and the fix that made it worse
The obvious repair is to have something in the flow carry the id forward. That was tried, and it is worth knowing how it went, because it is the more instructive half:
| Attempt | Result | Verdict |
|---|---|---|
| Ask the agent to state the record id | It stated the account's NAME | Wrong, but obviously wrong |
| Tell it the id is a UUID, not the name | It produced a perfectly-formed UUID matching no record in the database | Wrong, and invisible |
| Look the record up in a real step, by name | The database returned the actual id | Correct |
Ask the agent to state the record id
- Result
- It stated the account's NAME
- Verdict
- Wrong, but obviously wrong
Tell it the id is a UUID, not the name
- Result
- It produced a perfectly-formed UUID matching no record in the database
- Verdict
- Wrong, and invisible
Look the record up in a real step, by name
- Result
- The database returned the actual id
- Verdict
- Correct
Tightening the instruction did not make the model more accurate. It made the error better camouflaged. The working fix does not ask the model for the identifier at all: the model supplies a search term, and a query step supplies the id. A wrong search term returns zero rows and fails loudly. A wrong UUID fails silently forever.
Why nobody caught it for weeks
The broken node sat behind a human-approval gate. Every automated test stopped at the gate, because a machine cannot click Approve. Every node before it was verified dozens of times. The one node nobody could reach was the one that did not work.
You have not tested the path you cannot reach. Whatever sits after your approval step, your error branch, or your rare condition is the least-tested code you own — and it is usually the code that writes things down. Reach it deliberately, by hand, at least once.
The debugging order that works
- Read the failing node's resolved config — what the templates became, not what you wrote.
- Find the empty field. An unhelpful 500 is usually a blank where an id should be.
- Check the referenced node is in the log at all. If it is missing, it is a tool, and no wording of the reference will fix it.
- Only then read the node's code. You will almost never get this far.
Next: the capstone — the whole desk, built by you, with every one of these habits in it.
Related lessons
Base rates — what a piece of evidence is actually worth
A face-recognition system that is 99.9% accurate and almost entirely wrong, and a number that sent an innocent woman to prison. Both are the same arithmetic, and it is the arithmetic that decides what any piece of evidence is worth.
ReadConfirmation and survivorship — what you never looked for
Two questions about evidence you did not go looking for. One is a rule you have to discover, and one is a pattern in five famous people — and in both, the thing that would have told you the truth is the thing nobody checks.
ReadLoss aversion, sunk cost and regression — what it costs you
Four questions you answer about yourself rather than about a scenario, and your own answers are the finding. Then the pattern that makes praise look useless and criticism look like it works, whatever you actually do.
Read
