MoonshotAI / MoonshotAI/kimi-code
bug: quota-exhausted 429 is retried for ~3 minutes with no UI feedback (session looks frozen)
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 7.5k
- Forks
- 1.2k
- Avg merge
- 11h 53m
- Merged PRs (30d)
- 350
Description
Environment
- kimi-code 0.26.0 (darwin/arm64), provider
type = "kimi"againsthttps://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)
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).- The engine does emit
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
- Fail fast on quota-exhausted 429 (
exceeded_current_quota_error,insufficient_quota; billing-anchored message-pattern fallback), keeping transient rate-limit retry behavior unchanged — PR #1857. - 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.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with normalizeAPIStatusError and the OpenAI error conversion seam in packages/kosong/src/errors.ts, then inspect the turn.step.retrying case in session-event-handler.ts. Verify that structured quota errors fail without retries while transient 429s retain retry behavior, and that the TUI displays retry attempt and delay information.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 45/100