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

# Troubleshooting

> What to check when track_ai doesn't show up in the dashboard, or when fields look wrong.

When `track_ai` data doesn't show up or lands wrong, the cause is usually one of a handful of things. Find your symptom below.

## Nothing shows up in the dashboard

Walk these five top to bottom. The fix is almost always one of them.

### You didn't call flush() before exit

The exporter batches spans. On `os._exit()`, a Lambda timeout, or a script that ends right after `track_ai`, the daemon worker is killed before its next 5-second tick and the queue is lost. Flush before you exit:

```python theme={null}
import bentolabs_sdk.analytics as bento
bento.track_ai(...)
bento.flush()  # or bento.shutdown() which flushes too
```

Long-running services don't need this. `atexit` runs the shutdown for you.

### BENTOLABS\_API\_KEY isn't set in the running process

Setting it in `~/.zshrc` doesn't help if your IDE or CI launched the process from a different env. Check it from inside the process:

```python theme={null}
import os
print(os.environ.get("BENTOLABS_API_KEY"))
```

The key must start with `bl_pk_`. Otherwise the SDK raises `BentoAuthError("invalid_api_key_format")` on construction.

### BENTOLABS\_BASE\_URL points at the wrong host

It defaults to `https://api.bentolabs.ai`. If you set it for local dev and forgot to unset it in production, your spans go nowhere. Print what the SDK resolved:

```python theme={null}
from bentolabs_sdk import resolve_options
print(resolve_options().base_url)
```

### The daemon worker isn't running

List the threads after init and look for the worker:

```python theme={null}
import threading, bentolabs_sdk.analytics as bento
bento.init()
print([t.name for t in threading.enumerate()])
# Should include: 'OtelBatchSpanRecordProcessor'
```

If the worker thread isn't there, `init()` failed silently (a suppressed exception in your code) or you called SDK functions before init resolved.

### Spans are being dropped

The queue holds 2048 spans. Past that, drops are logged at WARNING level:

```
Queue full, dropping span.
```

Turn on Python logging to see it:

```python theme={null}
import logging
logging.basicConfig(level=logging.WARNING)
```

If you're hitting this, you're emitting faster than network egress. Reduce volume or talk to us about higher-throughput tiers.

## Fields look wrong in the dashboard

The spans arrived, but a column is empty or a value lands in the wrong place. Match your symptom below.

### The provider column is empty

Bento doesn't infer the provider from the model name. Pass `provider="anthropic"` (or `openai`, `google`, `aws_bedrock`, and so on) on every `track_ai` call. See [Attributes](/concepts/attributes).

### A conversation appears as N separate rows

You're not passing `convo_id`. Add the same `convo_id` to every turn in the conversation, including assistant messages and tool calls.

### The user filter doesn't work

Either `user_id` is missing, or you're passing it as a property (`properties={"user_id": ...}`). It has to be the top-level `user_id=` kwarg so it lands on `gen_ai.user.id`.

### A Bedrock model shows up under 'anthropic' instead of 'aws\_bedrock'

Bedrock model IDs like `anthropic.claude-3-sonnet-20240229-v1:0` look like Anthropic to us but route through AWS. Pass `provider="aws_bedrock"` explicitly.

### Custom properties show as strings even though you passed ints

Properties that are `int`, `float`, `bool`, or `str` keep their type. Dicts and mixed lists fall back to a JSON string. See [Properties → Type fidelity](/python/properties#type-fidelity).

## Spans nest weirdly

The spans show up, but the tree isn't what you expected. The three common shapes, and what's behind each:

### track\_ai calls don't show as children of your begin() block

Check your task context. The trajectory's parent context lives in a `ContextVar`, which is per-thread for sync code and per-task for asyncio. Calling `track_ai` from a new thread or a `concurrent.futures` worker that didn't inherit your context produces a root span instead. See the [threading model](/python/threading-model#async-and-context) for the full rules.

### track\_ai inside a FastAPI handler steals your framework span

It doesn't. `track_ai` and `begin` both detach from the caller's OTel context on purpose, so your customer's FastAPI or Django instrumentation isn't pulled into our trace. To make the Bento span a child of a parent OTel span, set its `traceparent` explicitly through the lower-level [OTel transport](/python/otel-transport).

### finish() raises RuntimeError 'out of order'

A nested `bento.begin()` is still open. Trajectories must be finished LIFO. Use `with bento.begin(...) as i:` to guarantee correct nesting; the context manager handles cleanup on exception too.

## Local development

Point the SDK at your local backend:

```python theme={null}
# Point at your local backend
import bentolabs_sdk.analytics as bento

bento.init(api_key="bl_pk_dev_...", base_url="http://localhost:8080")
bento.track_ai(event="local_test", input="hi", output="ok", user_id="me")
bento.flush()
```

Or set it through env vars:

```bash theme={null}
export BENTOLABS_API_KEY="bl_pk_dev_..."
export BENTOLABS_BASE_URL="http://localhost:8080"
```

Only `http://` and `https://` schemes are accepted. A typo like `BENTOLABS_BASE_URL=localhost:8080` (no scheme) raises `ValueError` on resolution.

## Still stuck

Email [support@bentolabs.ai](mailto:support@bentolabs.ai) with:

1. SDK version: `python -c "import bentolabs_sdk; print(bentolabs_sdk.SDK_VERSION)"`
2. The `track_ai` call you're making (redact secrets)
3. The output of `print([t.name for t in threading.enumerate()])` after init
4. Anything you see at WARNING-level Python logs
