Skip to content

[Bug]: the generic-OAuth hop permit is not threaded to the adapter, so one replay can be charged twice #4709

Description

@lidge-jun

Client or integration

Other

Area

Authentication and account pool

Summary

The generic-OAuth 429 ladder reserves a hop from the shared request budget, and the reservation is the charge (src/server/responses/adapter-dispatch.ts:719). The same budget is then handed to the adapter for the replay (:420), and an adapter that reserves for itself charges again — Kiro reserves immediately before its physical send at src/adapters/kiro-retry.ts:171. The non-adapter transient helper has the same shape: the hop is not supplied through pendingHopPermit, so its onSendsConsumed reporter adds a second charge.

One physical replay can therefore be counted twice, or refused as a second target transition. The passthrough ladder already solves this by reserving with countedExternally: true and handing the permit down through pendingHopPermit (src/server/responses/passthrough-dispatch.ts:1115).

This predates 2.56.0 and was found while fixing a related permit leak in that release; it is the remaining half of the same accounting story.

Reproduction

  1. Configure a generic-OAuth provider with two or more accounts and an adapter that reserves its own sends (Kiro), or any provider with a transient retry policy.
  2. Make the first send return 429 so the roster rotates.
  3. Watch the request's send accounting: the single replay is charged by the hop reservation and again by the adapter or by onSendsConsumed.

Suggested fix: mirror the passthrough shape — reserve the hop with countedExternally: true and pass it through pendingHopPermit so the layer that actually dispatches settles it.

Version

2.56.0 (e4a8539)

Operating system

macOS 15.5

Provider and model

kiro / any generic-OAuth provider with two or more accounts

Logs or error output

(accounting defect; visible as an early request_send_budget refusal or a doubled send count)

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Activity

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

    account-poolOAuth, credentials, Codex pool, quota, failover, plansbugSomething isn't workinglanded-via-maintainerOriginal PR closed after landing via a maintainer merge train

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions