#2932

fix(gateway): key limit and budget 429s now say which limit refused the request and when it resets

OpenDanielButler1wants to merge intofrom

https://entire.com/gh/phaseoteam/-/trails/165/changes/Phaseo/165

What changed

  • 429 key_limit_exceeded responses keep reason and reset_at, plus department_id and department_name when a department budget refused the request. They appear at the top level and under error.metadata. Other internal fields (current_value, buckets and so on) are still left out.

Why

The live enforce test after #3372 deployed (2026-10-29 16:18 UTC, owner's workspace, temporary key over its department's daily budget) returned:

429  retry-after: 27695
{"error":{"code":"key_limit_exceeded","message":"The Budget block test (temporary) department has reached its daily budget ($0.000173 of $0.00002).","type":"rate_limit_error","metadata":{"retry_after_seconds":27695}}, ...}

The guard builds reason, reset_at, department_id and department_name, but handleError passes only an allow-list of fields to the public body, and key_limit_exceeded had none. limits.mdx already promised reset_at for key limits and budgets, and now department_id/department_name too. So the body did not match the documentation for any key limit, not only department budgets.

How

core/error-handler.ts gets a field allow-list for key_limit_exceeded, mirroring the existing ones for rate limit policies and spend rule blocks. error-object.ts already copies reason and reset_at into error.metadata.

Before and after

429 key_limit_exceeded bodyBeforeAfter
reasonmissingdepartment_daily_cost_budget_reached
reset_atmissing2026-10-30T00:00:00.000Z
department_id / department_namemissingpresent for department budgets
error.metadataretry_after_seconds onlyplus reason, reset_at

Verification

  • New test in src/core/error-handler.test.ts ("keeps which key limit or department budget refused the request…"). It fails on main (1 failed) and passes with the fix. vitest run src/core/error-handler.test.ts → 36 passed.
  • tsc --noEmit (apps/api) → clean.
  • Live check after deploy: the same temporary key's 429 should carry the fields. The result will be posted here.

Proved with a focused test that fails without the fix; live check after deploy. Not run through sol-loop: it is a 7-line allow-list addition mirroring the two existing ones.

Model: Claude Opus 5.5 (medium effort) · Harness: Claude Code in T3 Code

🤖 Generated with Claude Code

Activity

7
vercel[bot]1d ago

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated
phaseo ReadyPreviewOct 29, 2026 4:33pm UTC

chatgpt-codex-connector[bot]1d ago

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

ReviewStatusCommitReview trigger
📝 Code Review✅ Completed 2026-10-29T16:23:09.647284Z84263aePR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

coderabbitai[bot]1d ago

WARNING

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 54 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 0cded5ca-9e97-41e8-bc4e-8434d6237eac
Commits

Reviewing files that changed from the base of the PR and between 7cf419141310a9d6876ab025b88f9c92a1efed21 and 84263aeee39fc1a72c4a98a2af4f8bde9fb2b76c.

Files selected for processing: 3
  • .changeset/key-limit-error-fields.md
  • apps/api/src/core/error-handler.test.ts
  • apps/api/src/core/error-handler.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

github-actions[bot]1d ago

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

Scanned Files

None
DanielButler11d ago

Live check after deploy (2026-10-29 17:39 UTC; temporary key in a temporary department over its daily budget):

{"code":"key_limit_exceeded","reason":"department_daily_cost_budget_reached","reset_at":"2026-10-30T00:00:00+00:00",
 "department_id":"13dcf3fd-…","department_name":"Budget block test (temporary)",
 "error":{"metadata":{"reason":"department_daily_cost_budget_reached","reset_at":"2026-10-30T00:00:00+00:00","retry_after_seconds":22828}}}
Retry-After: 22828

Before this deploy the same request's body had only the description and retry_after_seconds.