stablyai/orca · warning · CodexResetCreditScopeRejection

targetChanged

targetChanged

Error message

The active Codex rate-limit target changed before reset.

What it means

A CodexResetCreditScopeRejection with reason 'targetChanged', thrown by validateResetCreditScope. The runtime target currently reported by rateLimits.getState().codexTarget (its runtime + wslDistro) no longer equals expectedScope.target. The reset credit is bound to a specific runtime, so the provider call is refused before it starts to avoid charging the wrong target's credit.

Source

Thrown at src/main/codex-accounts/service.ts:545

        attempt.promise = null
      },
      () => {
        attempt.promise = null
        if (attempt.state === 'fresh') {
          this.releaseFreshResetAttempt(idempotencyKey, attempt)
        }
      }
    )
    return promise
  }

  private validateResetCreditScope(
    expectedScope: CodexResetCreditExpectedScope,
    requireCurrentOffer: boolean
  ): { managedHomePath: string; rateLimits: RateLimitState } {
    const rateLimitState = this.rateLimits.getState()
    if (!sameRateLimitTarget(rateLimitState.codexTarget, expectedScope.target)) {
      throw new CodexResetCreditScopeRejection(
        'targetChanged',
        rateLimitState,
        'The active Codex rate-limit target changed before reset.'
      )
    }

    const settings = this.store.getSettings()
    if (
      getSelectedCodexAccountIdForTarget(settings, expectedScope.target) !== expectedScope.accountId
    ) {
      throw new CodexResetCreditScopeRejection(
        'accountChanged',
        rateLimitState,
        'The selected Codex account changed before reset.'
      )
    }
    const account = settings.codexManagedAccounts.find(
      (candidate) => candidate.id === expectedScope.accountId

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Rebuild the expectedScope from the CURRENT rateLimits.getState().codexTarget and start a fresh attempt with a new idempotency key.
  2. If the target change was unintentional, restore the previously-active target (distro/runtime) and replay the same idempotency key.
  3. For fresh attempts, the rejection is returned as a rejectedBeforeProvider result with retryDisposition 'discardAttempt' (service.ts:476) — discard and let the user re-trigger from the current target.

Example fix

// before: replaying a stale scope after runtime switched
service.consumeRateLimitResetCredit(oldKey, staleScope) // rejects targetChanged

// after: rebuild scope from current target
const state = rateLimits.getState()
const scope = buildCodexResetCreditExpectedScope({ target: state.codexTarget, account, limits: state.codex })!
await service.consumeRateLimitResetCredit(crypto.randomUUID(), scope)
Defensive patterns

Strategy: validation

Validate before calling

// Rebuild the scope from the CURRENT target right before consuming.
const { codexTarget } = rateLimits.getState()
const scope = buildCodexResetCreditExpectedScope({ target: codexTarget, account, limits: rateLimits.getState().codex })
if (!sameRateLimitTarget(codexTarget, scope!.target)) {
  throw new Error('target drifted; refresh and retry')
}
await service.consumeRateLimitResetCredit(crypto.randomUUID(), scope!)

Type guard

const isSameTarget = (a: RateLimitRuntimeTarget, b: RateLimitRuntimeTarget): boolean =>
  a.runtime === b.runtime && a.wslDistro === b.wslDistro

Try / catch

try {
  await service.consumeRateLimitResetCredit(key, scope)
} catch (error) {
  if (error instanceof CodexResetCreditScopeRejection && error.reason === 'targetChanged') {
    // rebuild scope from current target and retry once with a fresh key
  } else throw error
}

Prevention

When it happens

Trigger: Consuming a reset credit (consumeRateLimitResetCredit replay, or a fresh startResetCreditAttempt) after the active Codex rate-limit target changed between when the scope was captured and when validation runs — e.g. the user switched the active WSL distro or moved from host to WSL runtime mid-flow.

Common situations: User toggles the runtime/distro selector, or an external CODEX_RUNTIME/WSL_DISTRO change flips codexTarget while a reset is queued behind the mutation queue. Also after an app restart that rehydrates a providerPending attempt whose target is no longer active.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/17c6b368e7c1de15. Report an issue: GitHub.