Skip to content

[Bug]: Claude usage lists a <synthetic> model with no explanation #14670

Description

@tris203

Before submitting

  • I searched existing issues and did not find a duplicate.
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Run a Claude thread and interrupt a turn (Stop), then send another message such as continue.
  2. Open Usage and look at the Claude model breakdown.

Expected behavior

The model breakdown lists only real models, or anything else in it is explained.

Actual behavior

A model named <synthetic> appears in the Claude usage, with zero tokens and no cost. Nothing in the UI or in docs/user/usage.md says what it is.

Diagnosis

<synthetic> is not a model. Claude Code writes it as message.model on assistant messages it generates locally without calling the API. In the transcripts I checked, every occurrence was the placeholder text No response requested., written after [Request interrupted by user] and before the next prompt. These rows have all token counts at 0:

{"type":"assistant","message":{"model":"<synthetic>","role":"assistant","stop_reason":"stop_sequence","usage":{"input_tokens":0,"output_tokens":0,"cache_creation_input_tokens":0,"cache_read_input_tokens":0}}}

The Claude transcript parser keeps any assistant row with a non-empty model name and has no zero-token check, so these rows become a usage bucket:

https://github.com/pingdotgg/t3code/blob/53456bc012/apps/server/src/usage/usageTranscripts.ts#L122-L123

Pricing already knows about the name and treats it as unpriceable, which is why it shows no cost:

https://github.com/pingdotgg/t3code/blob/53456bc012/apps/server/src/usage/usagePricing.ts#L145-L160

AgentSessionScanner.ts also already ignores it when picking a session's model.

Decision needed

Two ways to resolve this, maintainer's call:

  1. Exclude it. Skip <synthetic> rows in the Claude transcript parser so the name never reaches the Usage page. Nothing is lost from the totals, since the rows carry zero tokens and were never billed.
  2. Explain it. Keep the row and add a tooltip on the model name saying it marks messages Claude Code generated locally that were never sent to the API or billed.

Impact

Cosmetic issue

Version or commit

main @ 53456bc

Environment

Linux, Claude Code 2.1.284

Workaround

None needed; the row does not affect token or cost totals.

Activity

  1. juliusmarminge commented on Oct 1, 2026

    @juliusmarminge
    Member

    Note

    Grok responding on behalf of Julius.

    Triage

    Thanks for the clear diagnosis and for laying out both options, @tris203! I confirmed this on current main (a3abb52660). <synthetic> isn't a model. Claude Code writes it on assistant messages it makes locally, such as No response requested. after an interrupted turn, or a local auth or rate-limit notice. Those rows have zero tokens and were never billed.

    parseClaudeRecord in apps/server/src/usage/usageTranscripts.ts still keeps any assistant row with a non-empty message.model, so the name gets its own bucket in the Claude model breakdown. Pricing already refuses to price it (UNPRICEABLE_MODELS in usagePricing.ts), which is why the cost column says Unpriced and the token count stays at 0. The row leaves the token and cost totals unchanged. It does count toward the "unpriced records" share, though, because that share is based on how many records there are. Web and mobile both show the name exactly as stored, and docs/user/usage.md doesn't mention it.

    Recommendation

    I'd go with your first option: exclude it. Session import in AgentSessionScanner.ts already skips this placeholder when it picks a session's model, and the usage parser should do the same by dropping <synthetic> in parseClaudeRecord, with a parseClaudeLine test. A tooltip would put a local placeholder on both web and mobile without telling anyone anything useful about usage. One thing to keep in mind: when a real, billed response contains the text No response requested., it's stored under the actual model name, so it should still be counted.

    Codex and Grok already drop zero-token usage events, so this only affects Claude.

    Related

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 1, 2026
  3. added 2 commits that reference this issue on Oct 4, 2026
    ea65739
    d9e0047
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions