MoonshotAI / MoonshotAI/kimi-code

bug: quota-exhausted 429 is retried for ~3 minutes with no UI feedback (session looks frozen)

Open
#1,860 0 comments 0 reactions 0 assignees View on GitHub

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" 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)

  1. 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).
  2. 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.

Proposed fixes

  1. 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.
  2. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.