You cannot diagnose a Moonshot API error from the status code alone. The same 429 may mean cluster overload, account throttling or insufficient balance; the same 401 may be a mistyped key or a mix-up between the China and international sites. In 2026, the platform migration and the retirement of old models have also made 404 errors noticeably more common.
This article follows a sequence of locate, classify, then fix. It breaks down five categories—401, 404, 429, context overflow and timeouts—and provides reusable retry code and a support-ticket checklist. Models and prices follow the model pricing page.
Note: Moonshot and Kimi are trademarks of their respective owners. Error types, rate-limit tiers and retirement dates may change. This article reflects the official documentation and common conditions as of October 2026; the official documentation takes precedence.
1. Capture three fields first
When troubleshooting, collect three things first: the HTTP status code, error.type, and the request identifier. Moonshot error responses usually contain error.message and error.type, though the exact fields depend on what the current API returns. The request identifier may appear in the error body, a response header or console logs; its location differs between versions, so do not assume there is only one source.
At minimum, log the timestamp with time zone, status code, error.type, model name, the site the Base URL belongs to, whether the request was streaming, SDK version, retry count, usage and request identifier. Never log the full key, and never log prompts containing sensitive information.
Only with these three things can you tell whether the problem is configuration, account or server side. The following sections go through each category.
2. 401: check the site first, then the key
Moonshot has a China site and an international site: the China site is platform.kimi.com with the endpoint https://api.moonshot.cn/v1; the international site is platform.kimi.ai with https://api.moonshot.ai/v1. Accounts, balances and keys are completely separate. Using a China-site key against the international endpoint returns 401 invalid_authentication_error.
Troubleshoot in this order:
- Does the key come from the site matching the current Base URL, rather than the other site or another account?
- Are you mixing an open-platform key with a key from another product line such as Kimi Code? They are not interchangeable.
- Is the request header
Authorization: Bearer <key>, and are there spaces, line breaks or quotes around the value? - Is the environment variable actually loaded? Print the first few characters of the key to confirm.
- Has the key been deleted or rotated, or have project permissions been revoked?
- Is there an old proxy, old environment variable or routing tool configuration locally that is overriding the current request?
For error types, invalid_authentication_error usually means a key format issue or site mismatch, while incorrect_api_key_error usually means a missing or incorrect key. Fix the credential first in both cases; do not rush to retry.
3. 404: usually a retired model, not a vanished endpoint
The most easily overlooked source of 404 in 2026 is the model lifecycle. The retirement record on the official model page includes:
- The entire
moonshot-v1series, includingmoonshot-v1-8k,-32k,-128k,-autoand the vision preview, retired on August 31, 2026, with migration tokimi-k3recommended. - The
kimi-k2series, includingkimi-k2-0905-preview,kimi-k2-0711-preview,kimi-k2-turbo-previewand the thinking version, retired on May 25, 2026. kimi-latestwas retired on January 28, 2026, andkimi-thinking-previewon November 11, 2025.
Other common causes of 404:
- The OpenAI SDK did not set
base_url, so the request went to the default OpenAI endpoint and returned a model-not-found error. - The model name is misspelled, or the account tier has no access to that model.
- The path has an extra or missing
/v1, or the client concatenates the Base URL and full path twice. - The model names available on the China and international sites differ.
Use the same key to list models and confirm what the account can actually see:
curl "https://api.moonshot.cn/v1/models" \
-H "Authorization: Bearer $MOONSHOT_API_KEY"
Do not guess at models that are absent from the list. If the model list returns successfully but the chat endpoint still returns 404, focus on path concatenation and client configuration.
4. 429: three types, completely different handling
429 is the easiest category to misjudge during troubleshooting. Check error.type first:
error.type |
Meaning | Retry? | Handling |
|---|---|---|---|
engine_overloaded_error |
Cluster overload, tight capacity | Yes | Back off according to Retry-After, reduce concurrency |
rate_limit_reached_error |
Organization concurrency, RPM, TPM or TPD exceeded | Partly | Slow down, wait, raise the account tier |
exceeded_current_quota_error |
Arrears, insufficient balance, expired voucher | No | Check the balance and top up |
Moonshot's rate-limit tiers are divided from Tier0 to Tier5 by cumulative account top-up, and top-up vouchers usually do not count toward the cumulative amount. A tier constrains concurrency, requests per minute, tokens per minute and tokens per day at the same time: concurrency at low tiers may be a single digit and can reach the hundreds at high tiers; RPM ranges from single digits to hundreds, and TPM from hundreds of thousands to millions. Tier0 also has a daily token cap, and exceeding it usually means waiting for the next-day reset.
Several common pitfalls:
- The OpenAI SDK retries 408, 409, 429 and 5xx automatically by default, up to three attempts in total. For a low-tier account, one failure can be amplified into several requests and quickly exhaust the RPM. Set
max_retriesexplicitly in production. - Tools such as web search have their own QPS limits, calculated separately from the chat endpoint's RPM and TPM. Exceeding them also returns 429.
- Requests interrupted by a 429 are usually not billed, but a successful retry is billed normally. Do not retry indefinitely just because "it is not charged."
- If the limit does not match your tier, first check whether you are using another account's key.
5. 400: context overflow and parameter errors
Context overflow usually returns 400 invalid_request_error, and the error message indicates that the input token limit was exceeded. Note that the check is whether "input plus output limit" exceeds the model window, not just the input.
Common triggers:
- Conversation history grows without bound and multi-turn dialogs are never trimmed.
- An entire document is placed into a 1M context while a very large output limit is also set.
- A file upload exceeds 100MB, is empty, or has a
purposevalue outside the allowed list. - The output triggers
content_filter; this filter can also be triggered by model output, not just by checking input.
The handling order is: estimate input tokens first, then leave room for output; summarize long documents in segments and keep citations; use a sliding window for multi-turn conversations; use tool calling for structured output to reduce useless long text.
Also, finish_reason=length is not an error; it means the output reached its limit. If you need a longer result, request it in segments or use continuation features. Do not raise the limit above what the model allows.
6. Timeouts, 499 and streaming
Moonshot's gateway has a waiting limit for non-streaming requests. The official documentation notes that a 504 may appear after 900 seconds. Long-form generation, long-document analysis and coding agents are the most likely to encounter this.
Handle it in this order:
- Switch non-streaming calls to streaming with
stream=Trueso the connection keeps returning data. - Set a short connection timeout and a read timeout based on the longest generation time; do not keep the SDK defaults.
- Check whether a corporate proxy, gateway or local routing tool is closing the connection early. Such interruptions may appear as 499
client_closed_request. - Control the length of each generation with the output limit, and split overly long tasks.
- In streaming scenarios, watch time to first token; a sudden rise in TTFT usually indicates upstream congestion or a longer prompt.
- Log the request identifier and duration of every timeout, and distinguish a slow model from a slow network or a slow proxy.
Streaming does not lower the unit price, but it reduces the duplicate consumption caused by "waiting for a timeout and then retrying." Do not reuse a short non-streaming read timeout, or the client will cut the connection itself during normal generation.
7. How to write retries and backoff
Retries are only appropriate for transient errors. Do not automatically retry 401, 403, 404, content-safety errors or insufficient balance; classify 429 first; 5xx can be retried with backoff. For operations that have already produced side effects, use an idempotency key or a status query to confirm whether they ran.
import random
import time
from openai import OpenAI, APIStatusError, RateLimitError
client = OpenAI(
api_key="your-key",
base_url="https://api.moonshot.cn/v1",
max_retries=0, # disable SDK automatic retries
)
RETRYABLE_TYPES = {"engine_overloaded_error", "server_error",
"server_unavailable", "client_closed_request"}
def ask(prompt: str, retries: int = 4) -> str:
for attempt in range(retries):
try:
resp = client.chat.completions.create(
model="kimi-k3",
messages=[{"role": "user", "content": prompt}],
max_completion_tokens=1024,
)
return resp.choices[0].message.content
except RateLimitError as exc:
body = exc.body or {}
etype = (body.get("error") or {}).get("type", "")
if etype == "exceeded_current_quota_error":
raise RuntimeError("Insufficient balance or quota; top up first")
if etype == "rate_limit_reached_error":
raise RuntimeError("Account throttled; reduce concurrency or raise tier")
wait = min(2 ** attempt, 20) + random.random()
print(etype or "overloaded", "waiting", round(wait, 1), "seconds")
time.sleep(wait)
except APIStatusError as exc:
if exc.status_code in (401, 403, 404):
raise
if exc.status_code >= 500:
time.sleep(min(2 ** attempt, 20) + random.random())
else:
raise
raise RuntimeError("Retry attempts exhausted")
print(ask("Explain when retries are appropriate"))
When a Retry-After response header is present, wait according to it first, then add random jitter so that multiple instances do not retry simultaneously and cause a second wave of congestion. Cap the number of retries and send the final failure to alerting.
8. What to prepare before contacting support
- The time of occurrence, with time zone and year, accurate to the second.
- The request identifier, response
idandusagefields. - Model name, Base URL, site (China or international) and whether streaming was used.
- HTTP status code,
error.typeand the complete error message. - SDK and client versions, retry configuration, and whether a proxy or gateway was involved.
- A minimal reproducible request; a single curl command is enough.
- Billing or usage records, to check for duplicate charges.
- Sanitized logs with keys, user data and sensitive prompts removed.
Reproduce the issue once yourself with a direct curl, then submit the direct and gateway results separately. This can significantly shorten the time to diagnosis.
FAQ
Will retrying a 429 be billed twice?
Requests rejected with a 429 are usually not billed; a successful retry is billed as a normal request. The real concern is the SDK's automatic retry turning one business request into several, raising both request count and cost.
A model that worked yesterday returns 404 today. Why?
Check the retirement record on the official model page first. The moonshot-v1 series was retired on August 31, 2026, and the kimi-k2 series and kimi-latest have also been discontinued. Migrate to kimi-k3 as recommended.
A 429 says the balance is insufficient. Should I still back off and retry?
No. exceeded_current_quota_error is an account balance or quota issue, and retrying will not restore it. Check the balance, arrears status and voucher expiry first.
I am already using streaming. Why do I still get timeouts?
Streaming reduces the wait for the first packet; it does not promise that the connection will never drop. Check the proxy's idle timeout, read timeout, output limit and network stability. It may also be that the upstream has not produced a new token for a long time, causing an intermediate device to disconnect.
Direct connections work but the gateway returns errors. How do I determine responsibility?
Send the same key, model and request to both the direct connection and the gateway, and compare status codes, error.type, request identifiers and latency. If the direct connection succeeds and the gateway fails, focus on the gateway's path, authentication, timeout and retry configuration.
Next steps
Create a test key on the ZSQH AI Gateway, rehearse 401, 404, 429 and timeout scenarios once each, and then connect production monitoring. Available models and billing follow the model pricing page.
Related reading:
