dánwòdánwò

Fault matrix

Fault kinds

Every kind of fault the matrix can inject, what it simulates, and what it catches.

Every trial injects one fault. A fault is named by a label: what happens, then where it happens. For example, status 500 on POST api.stripe.com/v1/checkout/sessions.

Provider calls

Faults on a call your app makes to Stripe, Resend, Slack or another provider:

FaultWhat happensWhat it catches
status 500, status 503The provider fails.Errors shown as success, missing retries.
status 429The provider rate-limits the call.Retries that hammer the API, or none at all.
resetThe connection drops before the provider answers.Calls with no timeout or error handling.
reset-afterThe provider did the work, but the answer is lost.Retries that charge or send twice.
delay <ms>msThe answer is slow.Timeouts that are too short, or missing.

Each provider's error bodies match what its own SDK expects, so your app's error handling runs as it would in production.

Model APIs

Calls to OpenAI, Anthropic and the other model APIs can also get answers that go wrong. These check what your app does when the model answers badly.

FaultWhat the model sends
refusala refusal instead of an answer
empty-completionan answer with no text
truncatedan answer cut off at the token limit
content-filteran answer stopped by the provider's filter
malformed-tool-calla tool call whose arguments don't parse
stream-cuta stream that stops halfway

Webhooks

Faults on a webhook a provider sends your app:

FaultWhat happensWhat it catches
duplicateThe same webhook arrives twice.Work done twice, such as credits granted twice.
delay 600000msIt arrives ten minutes late.Pages that assume the webhook came first.
forgedA webhook arrives with a bad signature.Handlers that skip the signature check.
stale 3600sA correctly signed webhook from an hour ago.Handlers that accept replayed requests.

Races

Faults on timing, planned only where they can happen:

FaultWhat happens
double-submit on POST /buyA form or request is sent twice at once.
webhooks before redirectThe provider's webhooks arrive before the browser returns to your app.
reorder webhooks a, bTwo webhooks arrive the other way round.
slow-then-retryA payment call is slow; your app retries while the first one still succeeds.
2 concurrent usersThe whole test runs as two users at once. Ask for it with { concurrent: 2 }.

Database and cache

With danwo's data proxies in front of your app's Postgres and Redis, the matrix also faults the statements your test caused:

FaultWhat happens
reset-after on COMMITThe database committed, but your app never heard back.
serialization_failure, deadlockPostgres asks your app to retry the transaction.
down 3000msThe database is unreachable for three seconds.
OOM, READONLYRedis refuses a write.

See Database and cache to set this up.

Sockets and background jobs

FaultWhat happens
upgrade refused on WS …A WebSocket can't connect.
connection dropped on WS …A connected WebSocket drops.
status 500 on POST inngest.danwo.test/e/…A background job's event can't be sent.

Time

Faults don't move the clock. To test a trial that ends tomorrow or a token that expires, jump time forward inside the test. See Time travel.

Next

On this page

Run it on every pull request

A GitHub check and one comment per pull request, with the flight records attached.

Set up CI