# Error Injection

Test your application's error handling with one-shot errors, stream truncation, and timed disconnects. aimock provides three mechanisms for simulating failures.

## One-Shot Errors

Queue an error that fires on the next request and auto-removes itself. Useful for testing retry logic.

one-shot-error.test.ts ts

    const mock = new LLMock();
    await mock.start();
    mock.onMessage("hello", { content: "Hi!" });

    // Queue a 429 rate limit error for the next request
    mock.nextRequestError(429, {
      message: "Rate limit exceeded",
      type: "rate_limit_error",
    });

    // First request → 429 error
    const res1 = await fetch(`${mock.url}/v1/chat/completions`, {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        model: "gpt-4",
        messages: [{ role: "user", content: "hello" }],
      }),
    });
    expect(res1.status).toBe(429);

    // Second request → normal response (error auto-removed)
    const res2 = await fetch(`${mock.url}/v1/chat/completions`, { /* same */ });
    expect(res2.status).toBe(200);

## Stream Truncation

Abort a streaming response after a specific number of SSE chunks. Tests that your application handles partial streams gracefully.

truncation.test.ts ts

    mock.on(
      { userMessage: "long story" },
      { content: "This is a very long response that will be cut short" },
      { truncateAfterChunks: 3 }  // Abort after 3 SSE chunks
    );

## Timed Disconnect

Disconnect after a specified number of milliseconds. Simulates network timeouts and connection drops.

disconnect.test.ts ts

    mock.on(
      { userMessage: "slow" },
      { content: "This response will never complete" },
      { disconnectAfterMs: 100 }  // Kill connection after 100ms
    );

## Error Fixtures in JSON

fixtures/errors.json json

    {
      "fixtures": [
        {
          "match": { "userMessage": "error-test" },
          "response": {
            "error": {
              "message": "Rate limited",
              "type": "rate_limit_error"
            },
            "status": 429
          }
        },
        {
          "match": { "userMessage": "partial" },
          "response": { "content": "This gets cut off" },
          "truncateAfterChunks": 2
        },
        {
          "match": { "userMessage": "timeout" },
          "response": { "content": "Never finishes" },
          "disconnectAfterMs": 50
        }
      ]
    }

## Interruption Behavior

- `truncateAfterChunks` — counts SSE data lines sent; aborts on the Nth chunk
- `disconnectAfterMs` — starts a timer when the response begins; kills the connection when it fires
- If both are set, whichever fires first wins
- Interrupted requests are recorded in the journal with `response.interrupted: true` and `response.interruptReason`

`nextRequestError()` is one-shot: it fires once and auto-removes itself. For persistent error fixtures, use `addFixture()` with an error response.

See also: Chaos Testing — for probabilistic failure injection. Chaos testing adds configurable error rates, random latency spikes, and stream corruption that trigger based on probability rather than deterministic fixture matching. Use error injection for specific, reproducible failure scenarios; use chaos testing for resilience testing under unpredictable conditions.