Skip to content

fix(api): tolerate null resets_at for unused usage windows#18

Closed
jeanfw wants to merge 1 commit into
eddmann:mainfrom
jeanfw:fix/null-resets-at
Closed

fix(api): tolerate null resets_at for unused usage windows#18
jeanfw wants to merge 1 commit into
eddmann:mainfrom
jeanfw:fix/null-resets-at

Conversation

@jeanfw
Copy link
Copy Markdown
Contributor

@jeanfw jeanfw commented May 10, 2026

Summary

The Claude API legitimately returns resets_at: null on usage windows that have no consumption yet — typically when an account hasn't been used in the last 5 hours (so there's no active session window to reset). Today the mapper rejects these responses and surfaces:

Server response missing critical field: fiveHour.resetsAt

…which blocks refresh entirely for that account. The popover shows the orange Server returned invalid response banner and the user has no recourse short of using Claude on that account to force the API to populate the field again.

The fix

UsageAPIResponse.toDomain() now treats a null resets_at the same way it already treats a null sevenDaySonnet.resets_at: it falls back to now + window duration (5 hours for the session window, 7 days for the weekly window). A present-but-malformed date string is still treated as a hard error, since that's a real protocol violation.

Logic is factored into a small parseResetDate(from:field:formatter:fallback:) helper so all three windows use the same path.

Test plan

  • test_usageFetch_withMissingResetAt_fillsInFallbackWindow covers the new behaviour — null resets_at on the session window decodes to a future date roughly one session-window from now.
  • test_usageFetch_withMalformedResetAt_surfacesInvalidResponse ensures the strict path is still wired up — a non-ISO string surfaces AppError.networkError(.invalidResponse).
  • All existing tests still pass (the previous test_usageFetch_withInvalidPayload_surfacesInvalidResponse was renamed to make the intent unambiguous after this change).

🤖 Generated with Claude Code

The Claude API legitimately returns 'resets_at: null' for usage windows
with no consumption yet (e.g. a client account that hasn't been used
today). The mapper was rejecting these responses, surfacing 'Server
response missing critical field: fiveHour.resetsAt' in the popover and
blocking refresh entirely. Now a null reset_at falls back to now plus
the window duration (matching the existing behaviour for sonnet usage).
A present-but-malformed date string is still treated as a hard error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@eddmann
Copy link
Copy Markdown
Owner

eddmann commented May 18, 2026

Thanks Jean. This fix has been incorporated into #21 and merged there, with attribution preserved in the PR body and commit metadata.

I used the same core approach: tolerate resets_at: null by falling back to the relevant rolling usage window, while keeping malformed non-null reset dates as hard failures.

@eddmann
Copy link
Copy Markdown
Owner

eddmann commented May 18, 2026

Thanks again Jean. The null resets_at fix is now included in ClaudeMeter v1.3.1.

The released implementation follows the same core approach from this PR: tolerate null reset timestamps by falling back to the relevant rolling usage window, while keeping malformed non-null reset dates as hard failures.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants