Skip to content

JSON output gives no signal when a model has no price; cost silently reads as 0 #1736

Closed

Description

@ravsau

Summary

When a model has no known price, its usage is charged at 0. Table output prints a stderr warning ("Missing pricing for X; cost excludes this model"), but --json output carries no signal at all: the row shows "totalCost": 0 and the model breakdown looks identical to a free model. Scripts, dashboards, and the statusline JSON cache cannot tell "free" from "unknown", and the number silently understates spend until the pricing snapshot catches up.

This is the recurring shape behind most pricing issues here (new model lands, users see $0 for days, nobody notices in JSON). The detection already exists: ModelBreakdown.missing_pricing is computed for every entry, but it is #[serde(skip_serializing)] and only the table path reads it.

Reproduce

Any transcript with a model absent from the pricing map. On my machine a pi session logged openrouter/auto-beta:

ccusage pi monthly            # stderr: WARN  Missing pricing for [pi] openrouter/auto-beta ...
ccusage pi monthly --json     # no warning, no field, cost 0

Reproduced on main ef300c9 and on the published 20.0.20.

Proposal

Additive JSON fields, present only when relevant, so existing consumers and the 14 snapshot files that contain totals do not change for priced reports:

  • totals.unpricedModels: sorted, deduplicated model names, key omitted when empty. Scripts use .totals.unpricedModels // [].
  • modelBreakdowns[].missingPricing: true, omitted when false. Focused codex reports set the same flag on the affected entries of each period's models map.
  • --no-cost keeps both, since they describe availability rather than amounts.
  • Detection follows the cost mode: display mode and auto mode with stored costs look nothing up and report nothing. Docs state that an absent key means "no unpriced usage detected under this mode", not "every price is current".

Covers daily, weekly, monthly, and session reports for every agent, including --instances, --sections, and --by-agent. Two paths have no totals object and are left for a follow-up if wanted: blocks and session --id.

Two design questions for you

  1. Placement. The list lives inside totals next to the numeric aggregates. A consumer that iterates and sums every totals value would break only on a report with an unpriced model. Alternative: a top-level unpricedModels next to totals, or a pricing object. I went with totals because that is where scripts already look, and omit-when-empty keeps priced reports byte-identical. Happy to move it.
  2. Name. unpricedModels reads naturally with .totals; missingPricingModels would match the missingPricing flag exactly. Your call.

I have this implemented with tests (core, unified, codex, cost-mode suppression, zero-rate vs unknown, --no-cost survival) and a docs section, and will open the PR right after triage.

Activity

  1. pullfrog commented on Sep 16, 2026

    @pullfrog
    Contributor
  2. added
    priority:mediumA normal bug or meaningful improvement.
    triage:maintainableFits the repository's maintainable scope and needs review.
    on Sep 16, 2026
  3. added
    priority:lowMinor polish, documentation, or a low-impact edge case.
    triage:excludedExcluded from the current maintenance scope.
    and removed
    priority:mediumA normal bug or meaningful improvement.
    triage:maintainableFits the repository's maintainable scope and needs review.
    on Sep 16, 2026
  4. github-actions commented on Sep 16, 2026

    @github-actions
    Contributor

    Pullfrog triage: priority:low
    Kind: feature request
    Maintenance fit: excluded
    Confidence: high

    Request adds new optional JSON fields to signal unknown pricing. Table warning and zero cost already exist, so this is a new output surface for scripts and dashboards, not a fix to existing priced behavior. No maintainer endorsement in history, so excluded with low priority.

    Decision: closed by the contribution gate.

  5. added
    bugSomething isn't working
    priority:mediumA normal bug or meaningful improvement.
    triage:maintainableFits the repository's maintainable scope and needs review.
    and removed
    priority:lowMinor polish, documentation, or a low-impact edge case.
    triage:excludedExcluded from the current maintenance scope.
    on Sep 17, 2026
  6. ryoppippi commented on Sep 17, 2026

    @ryoppippi
    Member

    Reopening after maintainer review. The contribution gate misclassified this as a low-priority feature request.

    Current main already tracks missing pricing internally, but ModelBreakdown.missing_pricing is explicitly excluded from serialization, totals JSON carries no equivalent signal, and the missing-pricing warning is emitted only by table renderers. JSON and jq consumers therefore cannot distinguish a genuinely zero-priced model from usage whose price was unavailable, so automated reports can silently understate spend.

    This is a bounded machine-readable reporting correctness issue, not a new source or a broad product feature. I have reclassified it as bug, priority:medium, and triage:maintainable.

    The exact JSON shape still needs API review; reopening does not pre-approve the proposed field names or implementation. A focused PR should preserve existing output when all models are priced and cover shared and focused reports, jq output, --no-cost, and the distinction between an explicit zero price and missing pricing.

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 isn't workingpriority:mediumA normal bug or meaningful improvement.triage:maintainableFits the repository's maintainable scope and needs review.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions