> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zespan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Troubleshooting

> Solutions to the most common problems when integrating Zespan with your agents — from missing traces to cost calculation issues and broken span trees.

## Traces not appearing in the dashboard

**Symptom:** You initialized the SDK and made LLM calls, but the dashboard shows no data after 15–30 seconds.

<AccordionGroup>
  <Accordion title="Wrong or missing API key">
    Verify the API key you passed to `init()` starts with `zsp_` and matches the one shown in **Settings → API Keys** for your project. A typo or a key from a different project will cause all events to be silently dropped (the SDK logs a warning, but does not throw).

    Enable `debug: true` to see SDK activity:

    ```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    zespan.init({
      apiKey: process.env.ZESPAN_API_KEY!,
      debug: true, // logs flush success/failure to console
    });
    ```
  </Accordion>

  <Accordion title="Process exited before flush">
    The SDK batches events and flushes them every 2 seconds by default. If your script or Lambda handler exits before the flush fires, events are lost. Call `flush()` explicitly before the process ends:

    <Tabs>
      <Tab title="TypeScript">
        ```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
        await zespan.getClient().flush();
        ```
      </Tab>

      <Tab title="Python">
        ```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
        zespan.get_client().flush()
        ```
      </Tab>
    </Tabs>

    See the [Serverless guide](/guides/serverless) for environment-specific patterns.
  </Accordion>

  <Accordion title="Wrong baseURL">
    If you self-host Zespan, make sure you set `baseURL` to your API server's address. The default is `https://api.zespan.com`.

    ```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    zespan.init({
      apiKey: "zsp_...",
      baseURL: "https://api.yourdomain.com",
    });
    ```
  </Accordion>

  <Accordion title="Firewall or proxy blocking outbound requests">
    The SDK sends events to `https://api.zespan.com` over HTTPS (port 443). If your environment blocks outbound traffic, allowlist this endpoint. Use `debug: true` to see if flush requests are failing with network errors.
  </Accordion>

  <Accordion title="SDK not initialized before LLM call">
    `zespan.init()` must be called before any wrapped LLM client is created.

    ```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    // ✓ Correct — init before wrap
    zespan.init({ apiKey: "zsp_..." });
    const openai = zespan.wrapOpenAI(new OpenAI());

    // ✗ Wrong — wrap before init
    const openai = zespan.wrapOpenAI(new OpenAI());
    zespan.init({ apiKey: "zsp_..." });
    ```
  </Accordion>
</AccordionGroup>

***

## Cost showing \$0.00 for all events

**Symptom:** Traces appear in the dashboard but the `cost_usd` column is always zero.

<AccordionGroup>
  <Accordion title="Model name not in the pricing table">
    The SDK computes cost from a built-in pricing table keyed on the exact model string. If you use a model ID the SDK doesn't recognise, `cost_usd` is set to `0` rather than throwing an error.

    Check your model strings against the [supported models reference](/reference/models). Common mismatches:

    | You send            | SDK expects                  |
    | ------------------- | ---------------------------- |
    | `gpt4o`             | `gpt-4o`                     |
    | `claude-3-5-sonnet` | `claude-3-5-sonnet-20241022` |
    | `gemini-pro`        | `gemini-1.5-pro`             |

    If you use a model not in the table, open an issue on [GitHub](https://github.com/zespan) — we add new models with each release.
  </Accordion>

  <Accordion title="Token counts are zero">
    If `input_tokens` or `output_tokens` are `0`, cost will also be `0`. This can happen if the provider response doesn't include usage data — for example, streaming calls where you don't consume the full stream before closing it.

    For streaming calls, make sure you iterate the full response before the function returns:

    ```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    const stream = await openai.chat.completions.create({ stream: true, ... });
    for await (const chunk of stream) {
      // consume every chunk
    }
    // Usage data is in the final chunk — the SDK captures it automatically
    ```
  </Accordion>
</AccordionGroup>

***

## Parent-child spans not linking (broken trace tree)

**Symptom:** Multi-step agent workflows show as separate disconnected traces instead of a nested tree.

<AccordionGroup>
  <Accordion title="Context not propagated across async boundaries">
    The SDK propagates trace context automatically through async calls. If you break the async chain — for example, by using `setTimeout`, `setImmediate`, or a non-async callback — context is lost.

    Always use `await` for async operations inside `withZespanContext` or `withAgent`:

    ```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    // ✓ Context propagates correctly
    await withAgent({ name: "MyAgent" }, async (agent) => {
      await agent.traceTool("fetch-data", {}, () => fetchData());
    });

    // ✗ Context is lost inside setTimeout
    await withAgent({ name: "MyAgent" }, async (agent) => {
      setTimeout(() => fetchData(), 0); // no await — context is dropped
    });
    ```
  </Accordion>

  <Accordion title="withZespanContext missing in request handler">
    If you call wrapped LLM clients outside a `withZespanContext` block, each call gets its own isolated trace context and cannot link to siblings.

    Wrap your entire request handler:

    ```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    export async function POST(req: Request) {
      const { userId, sessionId } = await getSession(req);

      return withZespanContext({ userId, sessionId }, async () => {
        const summary = await summarize(text);
        const response = await generate(summary);
        return Response.json({ response });
      });
    }
    ```
  </Accordion>

  <Accordion title="withAgent not awaited">
    If you don't `await withAgent(...)`, the agent span may close before inner spans complete, breaking the parent-child link.

    ```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
    // ✓ Correct
    await withAgent({ name: "MyAgent" }, async (agent) => { ... });

    // ✗ Wrong — span closes immediately
    withAgent({ name: "MyAgent" }, async (agent) => { ... });
    ```
  </Accordion>
</AccordionGroup>

***

## Events missing in serverless (Lambda / Vercel / Cloud Run)

See the complete [Serverless deployment guide](/guides/serverless) for environment-specific patterns.

**Quick fix:** call `flush()` before your handler returns.

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
# Python Lambda
def handler(event, context):
    result = call_llm(event["prompt"])
    zespan.get_client().flush()  # must be last line before return
    return {"body": result}
```

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
// TypeScript Lambda / Vercel
export default async function handler(req, res) {
  const result = await callLLM(req.body.prompt);
  await zespan.getClient().flush(); // must await before responding
  res.json({ result });
}
```

***

## `401 Unauthorized` from the ingest endpoint

**Cause:** One of the following:

1. The API key was revoked or rotated and the old key is still in use
2. The `x-api-key` header is missing from the request
3. The key belongs to a different project

Go to **Settings → API Keys**, check that the key prefix matches the one your application is using, and rotate if necessary.

***

## Rate limit `429` with "Monthly quota exceeded"

This is **not** a per-minute rate limit — it means your organization has exhausted its monthly event quota. Retrying later in the same month will not succeed.

Options:

* Upgrade your plan from **Settings → Billing**
* Reduce your SDK `sampleRate` to send fewer events: `zespan.init({ sampleRate: 0.5 })`
* Wait for your quota to reset at the start of next month

***

## Prompt text not showing in trace detail

**Cause:** Prompt storage is on by default but may have been disabled. Verify `storePrompts` is not set to `false` in your `init()` call — PII redaction is applied to all stored text before transmission:

```typescript theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
zespan.init({
  apiKey: "zsp_...",
  storePrompts: true, // default — set false to disable
});
```

Review your data retention policy before enabling this. Once stored, prompt text is subject to your plan's retention window.

***

## Python SDK not capturing async OpenAI calls

`zespan.patch_openai()` patches both sync and async clients. If async calls are still not traced, verify:

1. `patch_openai()` is called before any `openai` module is imported in your actual call path
2. You are using `openai.AsyncOpenAI()` (not a pre-initialized client from before the patch)

```python theme={"theme":{"light":"github-light","dark":"one-dark-pro"}}
import zespan
zespan.init(api_key="zsp_...")
zespan.patch_openai()

# Create client AFTER patching
import openai
client = openai.AsyncOpenAI()  # ✓ this is patched
```

***

## Still stuck?

Enable `debug: true` in your SDK init and share the console output. Open an issue on [GitHub](https://github.com/zespan) or email [support@zespan.com](mailto:support@zespan.com).
