fix(claude/oauth): fall back when five_hour window is missing from response#741
fix(claude/oauth): fall back when five_hour window is missing from response#741Sanjays2402 wants to merge 2 commits intosteipete:mainfrom
Conversation
…sponse Previously `mapOAuthUsage` threw `ClaudeUsageError.parseFailed` whenever the OAuth usage endpoint omitted the `five_hour` block, even when the response still contained weekly or model-specific data. Users on plans that don't surface a five-hour window \u2014 or whose accounts are in a state where Anthropic temporarily omits it \u2014 saw the entire snapshot dropped, with weekly and Sonnet/Opus utilisation hidden. Fall back through the available windows (seven_day, seven_day_sonnet, seven_day_opus, seven_day_oauth_apps) when five_hour is absent, so the menu bar surfaces whatever the API did return. If every window is missing we still surface a parse error. Mirrors the tolerant decoding pattern added for Codex OAuth in steipete#710. Fixes steipete#726. Adds three regression tests: - five_hour absent, seven_day + sonnet present - five_hour present but without utilization - every window missing still throws
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3356613d04
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…ndow When five_hour is missing from the OAuth response, mapOAuthUsage now promotes a weekly window into snapshot.primary so the menu bar still renders. UsageStore.handleSessionQuotaTransition previously treated snapshot.primary as the session lane unconditionally, which meant the weekly fallback could fire spurious session-depleted/restored notifications on affected accounts. Guard sessionQuotaWindow on the primary window's windowMinutes: anything longer than ~6h is a weekly/model-specific fallback and must not drive session-quota transitions. RateWindow values with no windowMinutes keep the legacy behaviour to avoid regressions for providers that don't stamp a duration (e.g. Copilot tests use windowMinutes: nil). Adds two regression tests covering both directions of the guard.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b63e656a65
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| snapshot: UsageSnapshot) -> (window: RateWindow, source: SessionQuotaWindowSource)? | ||
| { | ||
| if let primary = snapshot.primary { | ||
| if let primary = snapshot.primary, Self.isSessionWindow(primary) { |
There was a problem hiding this comment.
Preserve session baseline when skipping non-session windows
Filtering snapshot.primary through isSessionWindow means Claude snapshots that only contain weekly fallback data now return no session window, which immediately clears lastKnownSessionRemaining in handleSessionQuotaTransition. In the real fallback scenario this can suppress a legitimate restored alert: if the user was previously depleted, one refresh with missing five_hour drops the baseline, and the next valid 5-hour snapshot is treated as first-seen data instead of a depleted→restored transition.
Useful? React with 👍 / 👎.
Summary
Fixes #726.
When the Claude OAuth usage response omits the
five_hourblock (or whenfive_houris present but has noutilizationvalue),mapOAuthUsagewas discarding the entire snapshot with
ClaudeUsageError.parseFailed("missing session data"),even if the response still contained
seven_day,seven_day_sonnet,seven_day_opus, orseven_day_oauth_apps. The user's menu bar thereforewent blank despite the API returning usable data — matching the exact
symptom reported in the issue ("raw response returned, but parser drops
it after a 5h query returns none").
Reproduction
Before this change, on
main:After this change the same input returns a
ClaudeUsageSnapshotwith theseven-day window promoted to
primary(and kept assecondary) and theSonnet window as
opus.Approach
mapOAuthUsagenow falls back through the available windows in a simplechain of
??expressions:secondaryandopusassignments are unchanged, so in the common case(five-hour present) the snapshot looks identical to today. The error is
only thrown when nothing at all is available.
This mirrors the tolerant decoding pattern added for Codex OAuth in #710.
Tests
Three new regression tests under
Tests/CodexBarTests/ClaudeUsageTests.swift:mapOAuthUsage falls back to seven-day window when five_hour is absentmapOAuthUsage falls back when five_hour has no utilization— the exactshape from the reporter's debug output
mapOAuthUsage throws when no windows are present— makes sure wedon't synthesise fake data when the response is empty
Build / verification
swift build→ clean on Swift 6.2 (Xcode 16.4, macOS 15.5)swiftformat --config .swiftformat <changed files>→ cleanClaudeUsageTestsand use theexisting
_mapOAuthUsageForTestingdebug hook; no new debug surfacewas needed.
Note on
swift test:swift testcurrently fails on this repo froma clean checkout (both on
mainand with this change) because theKeyboardShortcutsdependency's#Previewmacros can't resolvePreviewsMacrosunder plain SwiftPM. That's pre-existing and unrelatedto this fix — happy to adjust if CI has a different entry point I should
be running.
Scope
Code change is small and localized: 14 lines in
ClaudeUsageFetcher.swift,no public API or behavioural changes when
five_houris present.