A Tool Call Was Silently Failing, and My Agent Just Kept Going Like Nothing Happened
The setup
This agent handled inventory checks — a customer asks if something's in stock, the agent calls a checkInventory tool against our warehouse system, and answers based on the result. Simple, one tool, one job, been running fine for weeks. Then I noticed a pattern in support escalations: customers being told an item was in stock, ordering it, and then getting an "actually, we're out" email a day later from fulfillment. Not constant, but often enough to be a real pattern instead of noise.
Where I went looking first
My first assumption was a sync delay — inventory counts updating in the warehouse system slower than the agent was checking them, so it was reading stale-but-honest numbers. I checked the sync timestamps against the flagged orders. They lined up fine; the inventory data was current at the time the agent checked it. That ruled out staleness. Whatever was wrong, it wasn't a timing problem between two systems that were each telling the truth on their own schedule.
What was actually happening
I went and actually read the tool's code instead of just trusting its output, and found the real problem sitting in the error handling:
async function checkInventory(sku) {
try {
const result = await warehouseApi.getStock(sku);
return { inStock: result.quantity > 0, quantity: result.quantity };
} catch (err) {
console.error('Inventory check failed:', err);
return { inStock: true, quantity: 1 };
}
}
Whoever wrote this — me, a while back, on a different project this got copied from — had put a fallback in the catch block instead of letting the failure surface. If the warehouse API timed out, rate-limited, or returned anything unexpected, the function didn't throw or return an error state. It quietly returned { inStock: true, quantity: 1 } and moved on. From the agent's side, a genuine API failure and a real "yes, one's in stock" looked identical. The agent wasn't misreading anything — it was given a confidently fabricated answer by the tool itself, dressed up exactly like a real one.
I pulled the API logs and cross-referenced actual warehouse API errors against the flagged orders:
- Warehouse API calls in the flagged window: 1,240
- Calls that errored (timeout, 5xx, rate limit): 19
- Errors that returned the fallback "in stock" value: 19
- Of those 19, led to an oversell: 11
Every single error hit the fallback, because that's what the code did unconditionally. 11 of those 19 became a real oversold order. The other 8 happened to be for items that actually were in stock anyway, which is exactly the kind of thing that makes a bug like this invisible for a while — most of the time the wrong answer for the wrong reason still happens to be right.
The fix
1. Stop swallowing the error. Let it surface as a distinct state, not a guess.
async function checkInventory(sku) {
try {
const result = await warehouseApi.getStock(sku);
return { status: 'ok', inStock: result.quantity > 0, quantity: result.quantity };
} catch (err) {
console.error('Inventory check failed:', err);
return { status: 'error', inStock: null, quantity: null };
}
}
No more fabricated default. A failure now looks like a failure, not like a plausible answer.
2. Give the agent an explicit instruction for what to do with that failure state, instead of leaving it to infer something reasonable from a null:
If checkInventory returns status: "error", do not tell the customer the item is in or out of stock. Say you're having trouble checking current availability right now and offer to follow up shortly, or escalate immediately if the customer needs an answer right now. Never state stock status when the check itself failed.
3. Added alerting on the error rate itself, separate from the agent-facing fix — if checkInventory starts erroring above a small threshold in a short window, that pages someone directly, instead of the failures sitting invisible in a log nobody was tailing until customer complaints connected the dots.
Result
Re-ran the same time window's worth of call volume in a staging replay with the old warehouse API error conditions simulated. All 19 error cases now returned the honest "trouble checking" response instead of a fabricated stock answer, and zero of them would have led to an oversell. In the four weeks since shipping, warehouse API errors still happen at roughly the same underlying rate — that part wasn't something I could fix — but oversells traceable to this specific failure mode went to zero, and the alerting caught two real warehouse API outages early enough to route customers to a human before volume built up.
What I took away from this
This wasn't a prompting problem or a reasoning problem at all — the agent behaved exactly as it should have, given what it was told. The bug was a single fallback value written into a catch block, probably in a hurry, months earlier, that turned every kind of failure into a specific, plausible-sounding lie. That's the part worth internalizing: a try/catch that returns a default value instead of surfacing the failure doesn't just hide an error from a log, it hands the agent — and eventually the customer — a false fact with no way to tell it apart from a true one. If your agent is confidently wrong in a way that doesn't track with any obvious data problem, check what every tool actually does when it fails, not just what it returns when it works. A silent catch block is invisible until you go looking for it on purpose.
Comments
No comments yet — be the first.