{"record":{"id":"ec8a40f4a7b4cd96","repo":"can1357/oh-my-pi","slug":"usage-preflight-blocked-prefix-condition-for","errorCode":null,"errorMessage":"${USAGE_PREFLIGHT_BLOCKED_PREFIX} ${condition} for ${currentSelector}; reserve policy is fail-closed.","messagePattern":"(.+?) (.+?) for (.+?); reserve policy is fail-closed\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/session/turn-recovery.ts","lineNumber":1575,"sourceCode":"\t\t\t\thealth.accounts.some(account => account.state === \"healthy\")\n\t\t\t) {\n\t\t\t\tthis.#host.modelRegistry.authStorage.releaseSessionCredentialForReselection(\n\t\t\t\t\tcurrentModel.provider,\n\t\t\t\t\tthis.#host.sessionId(),\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\t\tif (health.state === \"unknown\") {\n\t\t\tthis.#usageReserveApprovedSelector = undefined;\n\t\t\treturn false;\n\t\t}\n\t\tif (health.state !== \"reserve\") this.#usageReserveApprovedSelector = undefined;\n\n\t\tconst reservePolicy = this.#host.settings.get(\"retry.usageReservePolicy\");\n\t\tif (reservePolicy === \"fail-closed\") {\n\t\t\tconst condition = health.state === \"reserve\" ? \"reserve reached\" : \"usage depleted\";\n\t\t\tthrow new Error(\n\t\t\t\t`${USAGE_PREFLIGHT_BLOCKED_PREFIX} ${condition} for ${currentSelector}; reserve policy is fail-closed.`,\n\t\t\t);\n\t\t}\n\t\tif (\n\t\t\treservePolicy === \"confirm\" &&\n\t\t\thealth.state === \"reserve\" &&\n\t\t\tthis.#usageReserveApprovedSelector === currentSelector\n\t\t) {\n\t\t\treturn false;\n\t\t}\n\t\tif (!this.#host.settings.get(\"retry.modelFallback\")) return false;\n\n\t\tlet fallback: { role: string; selector: RetryFallbackSelector; apiKey: string } | undefined;\n\t\tconst ceiling = this.#host.thinkingLevelCeiling();\n\t\tconst chainKeys = this.retryFallbackChainKeys(currentSelector, currentModel);\n\t\tfor (const role of chainKeys) {\n\t\t\tfor (const candidate of this.findRetryFallbackCandidates(role, currentSelector, currentModel)) {\n\t\t\t\tif (this.isRetryFallbackSelectorSuppressed(candidate)) continue;","sourceCodeStart":1557,"sourceCodeEnd":1593,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/session/turn-recovery.ts#L1557-L1593","documentation":"TurnRecovery performs a usage preflight before retrying a turn against a model (identified by currentSelector). When the health check reports 'reserve' (usage reserve reached) or depleted usage and the `retry.usageReservePolicy` setting is 'fail-closed', it throws immediately with the USAGE_PREFLIGHT_BLOCKED_PREFIX message — the retry is refused rather than spending tokens that may not be covered.","triggerScenarios":"A turn retry attempt while `#host.settings.get('retry.usageReservePolicy')` === 'fail-closed' and health.state is 'reserve' or usage is depleted for currentSelector (turn-recovery.ts:1571-1577).","commonSituations":"Long agent sessions exhausting the usage reserve with the default fail-closed policy; API quota nearly spent mid-batch; running unattended jobs where spend must not exceed the reserve.","solutions":["Free up quota: wait for the usage window to reset or top up credits/quota for the model selector.","Switch model to a selector with available reserve (retry with a different model).","Change retry.usageReservePolicy from 'fail-closed' to 'confirm' or a permissive policy if overspend is acceptable — set it in host settings.","Catch errors with USAGE_PREFLIGHT_BLOCKED_PREFIX and stop the run gracefully instead of looping retries."],"exampleFix":"// before\nawait session.retry(); // throws when reserve is hit under fail-closed\n// after: relax the policy or guard\nsettings.set('retry.usageReservePolicy', 'confirm');\nawait session.retry();","handlingStrategy":"try-catch","validationCode":"// before retrying, check the policy and stop early\nconst policy = settings.get('retry.usageReservePolicy');\nif (policy === 'fail-closed' && usageRemaining(selector) <= 0) {\n  return; // don't attempt the retry\n}","typeGuard":null,"tryCatchPattern":"const PREFIX = 'usage preflight blocked';\ntry {\n  await turnRecovery.retry();\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith(PREFIX)) {\n    logger.warn('retry blocked by usage reserve policy', { msg: err.message });\n    return; // stop cleanly; do not loop retries\n  }\n  throw err;\n}","preventionTips":["Monitor usage/health state and stop runs before the reserve is exhausted.","Choose retry.usageReservePolicy deliberately: fail-closed for unattended jobs, confirm for interactive use.","Configure fallback model selectors with remaining quota.","Alert on preflight-blocked messages so quota can be topped up promptly."],"tags":["usage","quota","retry","policy","billing"],"backgroundTag":"usage-quota-exceeded","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}