statusline: individual rate_limits windows are omitted instead of null, so a status line cannot tell "unknown" from "0% used"

Status Open
Reported on v2.1.260
Maintainer reply None cached
Activity 2 comments · opened Sep 4, 2026

The problem

In the statusline hook payload, each window inside rate_limits is emitted through a conditional spread. When a window has no data, the key is omitted from the object rather than emitted with a null value.

A status line therefore cannot distinguish two very different states:

  • the window is genuinely at 0% used, and
  • the window value is not known yet on this session.

Both look identical from the script's side: .rate_limits.five_hour is undefined.

Observed payload

Captured from a real session (~/.claude/statusline_debug.json, 2026-09-04T14:25:20+02:00, CLI 2.1.260, model claude-fable-5-1):

"rate_limits": { "seven_day": { "used_percentage": 47, "resets_at": 1788606000 } }

seven_day is present, five_hour is absent from the object entirely. Not null, not 0 — absent. The session was well into a working day, so 0% would not have been correct either; the value was simply not there.

Root cause in 2.1.260

The object literal that becomes rate_limits:

ko = {
  ...go.five_hour && { five_hour: { used_percentage: go.five_hour.utilization*100, resets_at: go.five_hour.resets_at } },
  ...go.seven_day && { seven_day: { used_percentage: go.seven_day.utilization*100, resets_at: go.seven_day.resets_at } },
  ...Ie() === "gateway" && go.overage && { spend_limit: { used_percentage: go.overage.utilization*100, resets_at: go.overage.resets_at } }
}

and the payload then carries it only if at least one key survived:

...(ko.five_hour || ko.seven_day || ko.spend_limit) && { rate_limits: ko }

So absence is overloaded: it means "no data" at the key level, and "no data for any window" at the object level.

Why this is worth fixing

The documented schema at statusline.md shows five_hour and seven_day as present fields with numeric values. A script written against that documentation reads .rate_limits.five_hour.used_percentage and gets undefined, with no way to tell whether that is a transient gap or a real zero. The only workaround is to render a placeholder such as ? and hope.

The convention for expressing "known to be unavailable" already exists in the same codebase: the get_usage control-protocol schema declares every window as .nullable().optional(), and pairs it with an explicit rate_limits_available boolean described as "False when plan rate limits do not apply (API key, Bedrock, Vertex, or missing profile scope) — rate_limits will be null."

Suggested fix

Either of these would resolve the ambiguity:

  1. Emit each known-but-unavailable window as null instead of omitting the key, matching the get_usage schema; or
  2. Add the rate_limits_available boolean to the statusline payload as well, so a script can tell "this plan has no rate limits" from "this window is not populated yet".

Environment

  • Claude Code 2.1.260
  • Windows 10 Pro 19045
  • Model claude-fable-5-1
  • Custom statusline script reading the hook payload from stdin

Related, but not the same

#45133 and #40094 are both closed and both describe rate_limits being missing in its entirety for certain auth or plan configurations. This report is narrower: the object is present and populated, and an individual window inside it is silently absent.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗