# Troubleshooting

Run tj doctor, read its exit codes, and fix the common daemon-lock, schema, capture, and MCP issues.

---

Most problems surface the same way: a blank dashboard, a command that hangs, or costs that don't add up. Start with `tj doctor`, then work through the specific issues below.

## `tj doctor`

`tj doctor` runs a battery of health checks and prints a status line for each one.

```bash
tj doctor
```

It checks:

- **Config file**: that it exists, parses as valid TOML, and has no conflicting overrides.
- **DuckDB writable**: that the database file can be opened. When the daemon is running it holds the write lock, so this check downgrades to "skipped" rather than failing (that's the expected state after `tj init`).
- **Schema vs capture**: flags an agent that declares an `output_schema` while `capture.tool_outputs` is false, since validation would have no data.
- **Drift readiness**: reports how close each agent is to its drift baseline threshold.
- **Webhook security**: checks alert webhook URLs and the domain allowlist.
- **Span stats and staleness**: surfaces an empty or stale database (no recent spans).
- **Schema integrity**: detects a migration recorded as applied whose column never actually landed (see [schema self-heal](#blank-status-page-schema-self-heal) below).
- **Onboarding first signal**: confirms tj has seen at least one span since setup.

### Exit codes

`tj doctor` sets its exit code so you can gate on it in scripts:

| Code | Meaning |
|---|---|
| `0` | Healthy. Everything passed. |
| `1` | Warnings. Nothing broken, but something worth a look. |
| `2` | Errors. At least one check failed. |

Add `--json` for machine-readable output, and `--repair` to attempt fixes for any issue that has a known repair path.

## The daemon holds the DB lock

DuckDB is single-writer. Only one process can open the database for writing at a time, and a running `tj serve` claims that lock. This blocks other tj commands that need write access, and can even block read-only opens.

Symptoms are a hanging command or an error mentioning a conflicting lock on `~/.tj/telemetry.duckdb`.

The fix is to stop the daemon before running a command that needs direct database access:

```bash
tj stop
```

`tj stop` halts the background daemon and sweeps any stray foreground `tj serve` process, freeing port 7391. Restart the daemon afterward with `tj serve` (or let the next login start it).

Many commands avoid this entirely. When `tj serve` is up, commands like `tj optimize` and `tj cost` fall back to the daemon's HTTP API instead of opening the database directly, so they keep working without a stop.

## Blank Status page (schema self-heal)

A blank Status page usually means spans are being dropped on write. This happens when a database migration was recorded as applied but its `ADD COLUMN` never landed, so every ingest that writes the missing column fails silently.

`tj doctor` catches this under its **Schema integrity** check. Fix it with:

```bash
tj doctor --repair
```

The repair rebuilds the affected table. Your data is preserved. tj also re-issues the additive column definitions on every open, so the gap normally heals itself before you notice it.

## Costs or content look wrong

If prompts, completions, or tool outputs aren't showing up where you expect, check your capture toggles. `prompts` and `tool_inputs` default on; `completions` and `tool_outputs` default off, so their text is absent until you ask for it.

```toml
[capture]
completions  = true
tool_outputs = true
```

Flip the flag for the content you want captured, then restart `tj serve` to pick up the change. Captured text only ever lives on your machine. See [Configuration](/docs/configuration) for the full list of toggles.

## MCP server won't connect

`tj mcp` chooses its connection mode at startup, and each mode changes what data the tools can see:

1. **Proxy to the daemon.** If `tj serve` is reachable, `tj mcp` proxies to it over HTTP and sees live ingest as it happens. This is the best case.
2. **Spawn the daemon.** If no daemon is running, `tj mcp` tries to start `tj serve` in the background and waits up to ten seconds for the port.
3. **Read-only fallback.** If it can't reach or start a daemon, it opens the DuckDB file directly, read-only. The read tools still work, but spans ingested after startup won't appear until you restart.
4. **No config.** If no config file is found, the tools return a no-config sentinel. Run `tj init` first.

If your MCP tools show stale data, you're likely in the read-only fallback. Start `tj serve` first, then restart the MCP client so `tj mcp` proxies to the live daemon.

## Still stuck?

Run `tj doctor --json` and include the output when you file an issue. It captures the config, database, and schema state in one shot, which is usually enough to pin down the problem.