You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
kimi-code 0.26.0 (darwin/arm64), provider type = "kimi" against https://api.moonshot.cn/v1, model kimi-k3
What happened
Our org ran out of token quota. From that moment every prompt — even a bare "hi" — made the TUI sit on the moon spinner for ~3 minutes and then fail. Users assumed the app was frozen; ps showed the process idle. Four consecutive turns reproduced identically.
Session log (timestamps UTC; one turn ≈ 189 s of silent retries):
11:51:30.355 INFO llm request turnStep=15.1
11:51:35.358 INFO llm request turnStep=15.1 attempt=2/10
...
11:54:34.426 INFO llm request turnStep=15.1 attempt=10/10
11:54:39.021 WARN llm request failed ... errorName=APIProviderRateLimitError
errorMessage="429 Your account org-… <ak-…> is suspended due to insufficient balance,
please recharge your account or check your plan and billing details" statusCode=429
11:54:39.037 ERROR turn failed turnId=15
Captured provider response (curl, same account)
HTTP/2 429 (no retry-after header)
{"error":{"message":"Your account org-… <ak-…> is suspended due to insufficient balance,
please recharge your account or check your plan and billing details",
"type":"exceeded_current_quota_error"}}
Earlier the same account produced a different wording for the same error.type: "You exceeded your current token quota: <org-…> …, please check your account balance" — one structured type, at least two message variants, so the message text alone is not a reliable discriminator but the structured type is.
Why it behaves this way (from source)
normalizeAPIStatusError classifies every 429 as a retryable APIProviderRateLimitError (packages/kosong/src/errors.ts) — the structured error.type/error.code parsed by the OpenAI SDK is dropped at the convertOpenAIError seam. A quota/balance 429 can never succeed on retry, yet it burns the full max_retries_per_step budget (10 attempts, 0.5→32 s backoff ≈ 3 minutes per turn).
The engine does emit turn.step.retrying with full attempt/delay data, but the interactive TUI drops it (session-event-handler.ts: case 'turn.step.retrying': break;), so even legitimate transient-429 retries look like a frozen app. Print mode and the v2 activity view both surface this event; the shell TUI is the only consumer that doesn't.
Show live retry progress in the TUI activity pane ("Rate limited (429) · attempt 3/10 · retrying in 12s") so genuinely transient retries are visible — companion PR incoming.
Environment
type = "kimi"againsthttps://api.moonshot.cn/v1, model kimi-k3What happened
Our org ran out of token quota. From that moment every prompt — even a bare "hi" — made the TUI sit on the moon spinner for ~3 minutes and then fail. Users assumed the app was frozen;
psshowed the process idle. Four consecutive turns reproduced identically.Session log (timestamps UTC; one turn ≈ 189 s of silent retries):
Captured provider response (curl, same account)
Earlier the same account produced a different wording for the same
error.type: "You exceeded your current token quota: <org-…> …, please check your account balance" — one structured type, at least two message variants, so the message text alone is not a reliable discriminator but the structured type is.Why it behaves this way (from source)
normalizeAPIStatusErrorclassifies every 429 as a retryableAPIProviderRateLimitError(packages/kosong/src/errors.ts) — the structurederror.type/error.codeparsed by the OpenAI SDK is dropped at theconvertOpenAIErrorseam. A quota/balance 429 can never succeed on retry, yet it burns the fullmax_retries_per_stepbudget (10 attempts, 0.5→32 s backoff ≈ 3 minutes per turn).turn.step.retryingwith full attempt/delay data, but the interactive TUI drops it (session-event-handler.ts:case 'turn.step.retrying': break;), so even legitimate transient-429 retries look like a frozen app. Print mode and the v2 activity view both surface this event; the shell TUI is the only consumer that doesn't.Proposed fixes
exceeded_current_quota_error,insufficient_quota; billing-anchored message-pattern fallback), keeping transient rate-limit retry behavior unchanged — PR fix(kosong): fail fast on quota-exhausted 429 instead of retrying #1857.