# Making the Mock Unpleasant on Purpose — Routebase

> Latency with a range, failures at a probability, a dead socket rather than a clean 500, and the 429 nobody handles. The four conditions a client only meets in production unless the mock produces them.

Canonical page: https://routebase.dev/guides/api-mocking/simulating-errors-latency-and-rate-limits/
Chapter 5 of 10 · API Mocking · Last reviewed 2026-09-13 · The Routebase Team

A client that has only ever seen fast, successful responses has not been built for the network. Every state it is missing is a state the mock declined to produce, and each one arrives at once when the real backend appears.

## The four conditions worth reproducing

Four properties of a real API have nothing to do with the shape of the response body, and all four are configuration rather than derivation.

**It is slow, and unevenly so.** Latency is not one number. A range is what surfaces the request that finishes after the one that started later, the spinner that appears for forty milliseconds and flickers, and the component that unmounts before its response arrives.

**It fails.** Not on a button you press, but occasionally, while attention is somewhere else. That is the difference between proving an error component renders and finding out what a retry does to a form somebody had half filled in.

**It refuses.** A request ceiling over a window produces the `429` that most clients handle worst, because the condition is invisible in development and unavoidable once real usage arrives.

**It breaks rather than answers.** A clean `500` is a response, and a connection that hangs until it times out or resets outright is not. Those take different paths through every HTTP client, and only one of them is usually tested.

## Why probability beats a switch

Triggering a failure deliberately tests the thing you were already thinking about, which is the error screen.

Everything else that failure touches is discovered by accident. A request failing mid-flow reveals that a form loses its contents on retry, that two components refetch the same resource independently, or that an optimistic update never gets rolled back. None of those are visible when you press the fail button and then look at the error state you expected.

A probability changes who finds the problem and when. Set it low enough that work continues and high enough that a failure lands within an hour, and the bugs arrive while somebody is in the middle of something rather than while they are auditing.

## The failures worth distinguishing

Not all failures are the same shape, and treating them as one category is how half the handling gets skipped.

| Failure | What the client sees | What it tests |
|---|---|---|
| `500` or `502` | A response with a status and usually a body | The error path and the message it renders |
| `503` | A response, sometimes with a retry hint | Whether the client backs off or hammers |
| `504` | A response after a wait | Whether the wait blocked the interface |
| Timeout | Nothing, until the client's own deadline | Whether there is a deadline at all |
| Connection reset | An abrupt transport error | The path that most clients never exercise |

The last two are the valuable ones and the ones most mocks cannot produce. A client with a well-tested `500` handler frequently has no timeout configured at all, which means a hung request holds a spinner open forever and nobody notices until a real network does it.

## Rate limits deserve their own pass

Rate limiting is the condition with the widest gap between how common it is in production and how often it is handled in a client.

The mechanics are simple enough. A ceiling over a window, a `429` once it is exceeded, and a `Retry-After` telling the caller when to come back. What a mock adds is the chance to find out what your client does with that, which is frequently to retry immediately and make the situation worse.

It is also the cheapest way to find accidental request volume. A screen that calls the same endpoint from four components will trip a low ceiling in development, where the fix is ten minutes, rather than in production, where the fix is an incident.

## Turning it up and down

A permanently hostile mock gets switched off, which returns you to the comfortable one.

The workable pattern is two postures. Everyday development runs with modest latency, a low failure probability and a rate limit that only a genuine mistake will hit, so the conditions are present without being the subject. Then a deliberate session turns the failure rate up, drops the ceiling and adds a timeout, and a developer spends an afternoon inside the conditions on purpose.

The reason to bother with either posture is the same. Integration day is mostly a catalogue of conditions the mock declined to show you, and every one of them you meet early is one you do not meet in the week before launch.

## In Routebase

Each rule carries its own behaviour, so one endpoint can be slow and unreliable while the rest of the mock stays quick.

_Screenshot: Error simulation, latency and rate limiting are sections of a single rule, and the two entries at the bottom of the error list are the ones a status code cannot express, because a timeout and a reset never arrive as a response at all._

**Error Simulation** takes a probability and an error type, where an empty probability means every request. The types cover `500`, `502`, `503` and `504`. Beyond those sit a timeout that holds the connection open before closing it and a connection reset that aborts immediately, which are the two failures a status code cannot express.

**Delay Settings** take a fixed delay in milliseconds plus an optional random maximum added on top, so responses vary the way they vary on a real network rather than arriving on a metronome. **Rate Limiting** caps a rule at a number of requests per second, minute or hour and answers everything past the ceiling with `429` and a `Retry-After` header until the window resets.

Because all three are per rule, a realistic profile is a mixture rather than a global setting. The checkout endpoint can be the slow and occasionally failing one while the reference data stays instant, which is closer to production than any single number would be. The [Mock Server](https://docs.routebase.dev/mock-server/) guide covers errors, delays and rate limits.

## Frequently asked questions

### How do you simulate API errors in a mock server?

Configure the failure as a probability rather than as a switch you flip while watching. A rule that fails one request in ten produces failures while somebody is working on an unrelated screen, which is when you find out what a retry does to a half-filled form. Triggering an error on demand only proves that the error component renders.

### Why would you add latency to a mock server?

Because a response that arrives in three milliseconds hides every problem that timing causes. No loading state gets built, a spinner that flickers for forty milliseconds is never noticed, and two requests that race are never out of order. A fixed delay with a random component on top reproduces the variability that makes those failures appear.

### How do you test how a client handles HTTP 429?

Give the mock rule a request ceiling over a window and let the client exceed it. Rate limits are the condition frontends handle worst, because they are invisible in development and unavoidable in production. A mock that enforces one turns 429 from something the team read about into something the interface has already survived.

### How often should a mock fail?

Often enough to be met by accident and rarely enough that work continues, which for most teams is a single-digit percentage. A mock failing half its requests gets switched off within a day, and a mock that never fails teaches the client that failure does not happen. The failure rate is a setting worth raising deliberately for a session rather than leaving high permanently.

---

[Routebase](https://routebase.dev/) — [Sign up](https://app.routebase.dev/): Every account starts with a 14-day Pro trial — no credit card required.
