can1357/oh-my-pi · error

${USAGE_PREFLIGHT_BLOCKED_PREFIX} ${condition} for ${current

Error message

${USAGE_PREFLIGHT_BLOCKED_PREFIX} ${condition} for ${currentSelector}; reserve policy is fail-closed.

What it means

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.

Source

Thrown at packages/coding-agent/src/session/turn-recovery.ts:1575

				health.accounts.some(account => account.state === "healthy")
			) {
				this.#host.modelRegistry.authStorage.releaseSessionCredentialForReselection(
					currentModel.provider,
					this.#host.sessionId(),
				);
			}
			return false;
		}
		if (health.state === "unknown") {
			this.#usageReserveApprovedSelector = undefined;
			return false;
		}
		if (health.state !== "reserve") this.#usageReserveApprovedSelector = undefined;

		const reservePolicy = this.#host.settings.get("retry.usageReservePolicy");
		if (reservePolicy === "fail-closed") {
			const condition = health.state === "reserve" ? "reserve reached" : "usage depleted";
			throw new Error(
				`${USAGE_PREFLIGHT_BLOCKED_PREFIX} ${condition} for ${currentSelector}; reserve policy is fail-closed.`,
			);
		}
		if (
			reservePolicy === "confirm" &&
			health.state === "reserve" &&
			this.#usageReserveApprovedSelector === currentSelector
		) {
			return false;
		}
		if (!this.#host.settings.get("retry.modelFallback")) return false;

		let fallback: { role: string; selector: RetryFallbackSelector; apiKey: string } | undefined;
		const ceiling = this.#host.thinkingLevelCeiling();
		const chainKeys = this.retryFallbackChainKeys(currentSelector, currentModel);
		for (const role of chainKeys) {
			for (const candidate of this.findRetryFallbackCandidates(role, currentSelector, currentModel)) {
				if (this.isRetryFallbackSelectorSuppressed(candidate)) continue;

View on GitHub (pinned to 9690622007)

Solutions

  1. Free up quota: wait for the usage window to reset or top up credits/quota for the model selector.
  2. Switch model to a selector with available reserve (retry with a different model).
  3. Change retry.usageReservePolicy from 'fail-closed' to 'confirm' or a permissive policy if overspend is acceptable — set it in host settings.
  4. Catch errors with USAGE_PREFLIGHT_BLOCKED_PREFIX and stop the run gracefully instead of looping retries.

Example fix

// before
await session.retry(); // throws when reserve is hit under fail-closed
// after: relax the policy or guard
settings.set('retry.usageReservePolicy', 'confirm');
await session.retry();
Defensive patterns

Strategy: try-catch

Validate before calling

// before retrying, check the policy and stop early
const policy = settings.get('retry.usageReservePolicy');
if (policy === 'fail-closed' && usageRemaining(selector) <= 0) {
  return; // don't attempt the retry
}

Try / catch

const PREFIX = 'usage preflight blocked';
try {
  await turnRecovery.retry();
} catch (err) {
  if (err instanceof Error && err.message.startsWith(PREFIX)) {
    logger.warn('retry blocked by usage reserve policy', { msg: err.message });
    return; // stop cleanly; do not loop retries
  }
  throw err;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/ec8a40f4a7b4cd96. Report an issue: GitHub.