---
title: "What 1.2 million agent tool calls taught us about timeouts, retries, and the true cost of a vague error message"
description: "Ninety days of production MCP traffic in numbers: retry storms, timeout cliffs, and why one ambiguous error string cost more than any outage did."
canonical: "https://getelva.ai/blog/what-1-2-million-tool-calls-taught-us"
author: "Elva Team"
published: "2026-04-16"
lastModified: "2026-09-01"
tags: "MCP, Observability"
---

# What 1.2 million agent tool calls taught us about timeouts, retries, and the true cost of a vague error message

**TL;DR**

- Agents retry fast: the median gap after a failure was 1.9 seconds, and 41% of retries carried identical arguments.
- Past roughly three seconds of p95 latency, retry traffic grows faster than useful traffic.
- Structured errors naming the offending field cut retry volume 71% on the worst tool; error messages are instructions.

Over ninety days we watched 1.2 million tool calls cross our hosted MCP servers, and three patterns dominated everything else. Agents retry fast. Timeouts cliff rather than degrade. And error messages are instructions, whether you meant them to be or not. The numbers below are from that window; the rest of this post is what each one means for an API that agents call.

## The numbers

| What we measured | Result |
|---|---|
| Tool calls observed | 1.2 million over 90 days |
| Median gap between a failed call and its retry | 1.9 seconds |
| Retries carrying byte-identical arguments | 41% |
| p95 latency past which retry traffic outgrew useful traffic | roughly 3 seconds |
| Share of failed-call retries caused by one vague error string | 34% |
| Retry reduction after replacing it with a structured error | 71%, within a week |

## Agents retry fast, and mostly identically

The median gap between a failed call and its retry was 1.9 seconds, and 41% of retries carried byte-identical arguments. A human who gets an error reads it, thinks, and changes something. An agent that gets an error very often sends the same request again, immediately, on the theory that the failure was transient. Sometimes it was. Often it was not, and the second call fails for the same reason as the first.

Two things follow. The first is that a retry after a timeout has to be a safe decision, which means idempotency has to be documented consistently with [RFC 9110's method semantics](https://www.rfc-editor.org/rfc/rfc9110#section-9.2.2) rather than assumed. The second is that rate limiting by raw request count stops fitting: a single user intent can legitimately fan out into a dozen tool calls, so limits work better per session or per task than per request, a point we made at more length in [MCP vs REST](https://getelva.ai/blog/mcp-vs-rest-api.md).

## Timeouts cliff rather than degrade

Past roughly three seconds of p95 latency, retry traffic grew faster than useful traffic on every server we measured. Below that line, slow calls were just slow. Above it, agents began treating slowness as failure, retried, and the retries added load that pushed latency further past the line. The transition was not gradual on any server we watched.

The operational consequence is that a latency budget for an agent-facing tool is not a target, it is a cliff edge, and the alerting threshold should sit well inside it. It is also why [MCP servers have to be run as infrastructure](https://getelva.ai/blog/mcp-servers-are-infrastructure.md): the failure mode is a feedback loop, and feedback loops do not wait for office hours.

## Error messages are instructions

The expensive lesson was the third one. A single vague message — "invalid request," with no field, no code, no hint — accounted for 34% of all failed-call retries in the window, because every agent that saw it guessed a different fix. Replacing it with structured errors in the style of [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457), naming the offending field and the allowed range, cut retry volume on that tool by 71% within a week. Nothing else we shipped all quarter moved a number that far.

The model reads your error string the way it reads your tool description: as text telling it what to do next. An error that names the field and the constraint is a correction. An error that says "invalid request" is an invitation to guess. This is also why machine-readable error shapes carry a hard cap in the [agent-readiness grade](https://getelva.ai/blog/scoring-agent-readiness.md): without one, nothing else about the API can compensate.

## FAQ

**How quickly do agents retry failed calls?**

The median gap between a failure and its retry was 1.9 seconds, and 41% of retries carried byte-identical arguments.

**What causes agent retry storms?**

Latency past roughly three seconds of p95, where retry traffic grows faster than useful traffic, and vague error messages: one string with no field or code accounted for 34% of all failed-call retries.

**What reduced retries the most?**

Structured errors in the style of RFC 9457 naming the offending field and the allowed range. That single change cut retry volume on the worst tool by 71% within a week.
