Make your agent fail closed
There is a category of agent bug that never shows up in your error rate, never trips an alert, and never gets filed. The agent calls a tool. The tool returns nothing useful — empty result, timeout, permission denied, malformed payload. The orchestration shrugs and lets the model answer anyway.
You get a fluent answer with no foundation under it. Nothing logged an error, because from the system's point of view nothing went wrong.
Fail open vs fail closed
Borrowed from access control, and the analogy holds exactly:
- Fail open — when the check fails, let it through. A door that unlocks in a power cut.
- Fail closed — when the check fails, deny. A door that stays locked.
Almost every agent framework defaults to failing open with tool results, and does it for a reasonable-sounding reason: a tool returning nothing is treated as "no additional context," which is a state the model is expected to handle gracefully. Graceful, here, means guessing.
The default is wrong for any tool whose entire job is grounding. If the retrieval tool is the reason you can trust the answer, retrieval failing has to be terminal.
Why this is worse than a plain hallucination
An unprompted hallucination at least has no institutional backing. This failure mode produces something more expensive: an answer that came out of a system explicitly built to be grounded, that the reader has been told to trust because it's grounded.
You built the credibility. Then you attached it to a guess.
And it's silent by construction. Wrong answers get caught when someone knows the real number. The ones that don't get caught are the ones about topics where the model's pretraining is plausible — which is most topics.
Three places to enforce it
1. In the tool wrapper. Don't let a tool return an ambiguous empty. Distinguish, explicitly, the three cases: succeeded with rows, succeeded with genuinely zero rows, and failed to execute. Collapsing the last two is the root cause of most of this. "No results found" and "I could not query" are different facts and must not serialize to the same thing.
2. In the orchestration. Make a failed grounding tool short-circuit the turn. The agent should return the failure, not a synthesized answer. This is a control-flow decision and it belongs in code, not in a prompt asking the model to please be careful — a model under instruction pressure to be helpful will route around a politely-worded rule.
3. In the evals. Add a test that deliberately breaks the retrieval path — revoke the permission, point at a dead endpoint — and assert the agent refuses. If your suite has no test that expects a non-answer, you have no evidence the system can produce one.
That third one is the one teams skip, and it's the only one that keeps working after you leave.
The cost, stated fairly
Failing closed means more visible failures. Your dashboards will look worse. Someone will ask why the agent "stopped working" when in fact it started reporting a problem it always had.
That is the trade, and it's worth taking: a system that says "I couldn't reach the data" is operationally boring and trustworthy. A system that quietly guesses is pleasant right up until the number lands in front of someone who can check it.
The rule
If a tool exists to make an answer trustworthy, that tool failing must prevent the answer.
Everything else — retries, fallbacks, degraded modes — is fine, as long as the degradation is stated in the output rather than absorbed silently. The user's trust is calibrated to what you told them the system does. Keep those two things in sync.