Skip to main content

Troubleshooting

The hook shows nothing in the tool and always exits 0, even when the server rejects the request, so a broken setup is silent by design. Start with the quick checks, then run check, then go step by step.

Quick checks​

  1. which costpilot-agent must print a path. If not, the agent is not installed globally: repeat Step 1.
  2. Did you restart your tools after init? Settings and hooks are read at startup.
  3. Is your branch name carrying an issue key? Without one the session counts as "No issue"; the branch name is kept in the branch dimension.
  4. Did you give the telemetry up to a minute to arrive? Exporters batch.
  5. Is the dashboard's date range including today?

Run check in the project directory​

cd ~/code/my-project
costpilot-agent check

check covers every tool configured for the project or globally (--tool to pick one), says which scope is in effect and warns when a global config makes other projects report too. Each line is one of the checks below. ✗ lines make the command exit 1; ! lines are warnings.

LineWhat it verifiesIf it fails
costpilot-agent is on the PATHThe tool can spawn the hookInstall globally, or npm link a checkout
project config / global configWhich settings file is in effectRun init in the right scope
telemetry: on → URLExport is configured and points at your serverRe-run init --endpoint …
hook: … → URL/v1/session-mapThe prompt hook is registeredRe-run init
branch … → issue …What the hook would report for the current branchRename the branch or adjust the issue key pattern
server: accepted a test mapping (HTTP 200)A real POST /v1/session-map with your endpoint and tokenSee 401 and connection errors
<Tool> ran the hook N min ago: …The tool itself executed the hookRestart the tool and send one prompt
! turn-end hook: missing under <Event>The agent was upgraded but the tool's settings predate the turn-end hookRe-run init (or sync --project for a project setup)
! hook: old command without --endpoint / ! hook: posts to X, telemetry goes to YHook and telemetry disagree about the serverRe-run init --endpoint …
✗ not a git repository / ✗ no branch checked outThe hook has nothing to derive an issue fromRun check inside a repository on a branch
✗ settings file is invalidA tool's settings file is not valid JSON/TOMLFix the file by hand, then init again

The last line reads a receipt the hook leaves in ~/.costpilot/hook-runs/ on every prompt run (time, issue, HTTP status, never the token; turn-end runs leave none). ! not seen yet means the tool has not run the hook here yet. With a global setup check also prints a ! line saying that every project reports; that is a reminder, not an error.

The server answers 401​

The server sets COSTPILOT_INGEST_TOKEN and init was run without, or with the wrong, --token. Re-run init with the right token; the tools' settings and the hook command are updated together. Dashboard accounts are a separate thing: /v1/* is never behind the sign-in, and the bearer token is the only credential the agent needs.

The server is unreachable​

Try the same request the hook makes:

curl -i -X POST https://costpilot.example.com/v1/session-map \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer <secret>' \
-d '{"session_id":"test-1","issue_id":"PROJ-123","branch":"feature/PROJ-123-test","repo":"owner/name","agent":"claude-code"}'

A 200 with {} means the server is fine and the problem is on the tool side. A connection error means the endpoint is wrong or the server is down. The endpoint is always the API base URL without a path: the hook appends /v1/session-map itself, the tools append /v1/logs and /v1/metrics.

Cost arrives but is not attributed to the issue​

The telemetry shows up under the actor and model, but the Issue tile shows the session under "No issue":

  • The report must reach the server before the usage events it should apply to. The hook runs on every prompt, so after the first prompt everything that follows is attributed. Calls that happened before the first report of a session stay unattributed.
  • The hook ran but on the wrong branch: check the branch … → issue line of check. A branch created mid-turn is handled by the turn-end report, which only servers with turnEndReports: true receive.
  • The key does not match the pattern: the default issue key pattern and the project patterns live in the Admin Area. The whole key must match a project pattern for the project to be derived from it.

Claude Code project setup exports nothing​

Claude Code ignores CLAUDE_CODE_ENABLE_TELEMETRY and the OTEL_* variables in project-level settings. A project setup therefore relies on direnv and ./.envrc. check warns when the shell you run it in does not have the variables (direnv not hooked in, or direnv allow missing), and fails when the last hook run did not have them either: Claude Code was started outside a direnv shell. Start claude from a shell inside the project; in VS Code use the direnv extension or launch the editor with code . from that shell. See Report only some projects.

Tool usage card stays empty​

Claude Code's built-in tool names arrive without extra settings; MCP server names need OTEL_LOG_TOOL_DETAILS=1. init sets it unless the admin switched off Agent → Log tool details; check the env block of ~/.claude/settings.json (or ./.envrc for a project setup). Gemini CLI tool calls and Codex CLI tool results are counted as well; Cursor sends no telemetry at all.

Leftover hooks from an earlier setup​

init replaces its own hook entries and leaves everything else untouched. A hook from an earlier prototype (for example a node .../hooks/map-session.js line pointing at localhost:4318) keeps running next to the new one and has to be removed from the UserPromptSubmit block by hand.

Capture raw telemetry​

To see exactly what a tool sends, including attributes ingestion drops, set COSTPILOT_OTLP_DUMP on the server to a file path. Every /v1/logs and /v1/metrics request is appended to it as received, one JSON line per request ({ receivedAt, kind, payload }), before any normalization:

COSTPILOT_OTLP_DUMP=../otlp-dump.jsonl npm run dev # bash
$env:COSTPILOT_OTLP_DUMP="..\otlp-dump.jsonl"; npm run dev # PowerShell

Relative paths resolve against the API's working directory (apps/api). The payloads contain user identities and, with OTEL_LOG_TOOL_DETAILS=1, commands and file paths: keep the file outside the repository.

A tool exports its telemetry to one endpoint only. To capture from your own machine, point the agent at the local instance and back again afterwards; init keeps the configured token unless you pass a new one, and the tools pick up the change on their next start:

costpilot-agent init --endpoint http://localhost:8787 # to the local instance
costpilot-agent init --endpoint https://costpilot.example.com # back to the usual server
costpilot-agent check # shows where each tool reports

Run the hook by hand​

On a branch with an issue id:

echo '{"session_id":"test-1","cwd":"'$PWD'"}' | costpilot-agent hook --tool claude-code --endpoint https://costpilot.example.com --token <secret>

Then watch the dashboard or the server logs for session test-1. Each tool has its own payload shape; Cursor for example expects conversation_id and workspace_roots. Two side effects: the report creates a real turn for session test-1 on the server, and the run leaves a receipt, so the next check reports it as a hook run of Claude Code.