kimik3.io/Errors

Kimi K3 errors & failure modes

Every response body below came off the wire on 2026-07-16 from a call we deliberately broke. None of it is reconstructed from documentation.

The one that will actually get you isn't in this table's usual company: K3 returns HTTP 200 with an empty content when max_completion_tokens is too low for its reasoning to finish — and bills you in full. It is silent, it looks like success, and it is the most likely way a first integration breaks. Full write-up →

Measured against api.moonshot.ai with model kimi-k3how we measured.

The error shape

Errors come back as a single error object with message, type, and code:

{
  "error": {
    "message": "API key is missing or invalid",
    "type": "invalid_authentication_error",
    "code": null
  }
}

Do not switch on code. It is nullable, and in every error we triggered against Moonshot direct it was absent entirely. type is the field that was always populated — match on that.

Through a gateway, the auth and model-not-found bodies below will not be byte-identical. A bad key is rejected by the gateway's own auth layer and never reaches Moonshot, so you get its wording, not Moonshot's. The symptoms, causes and fixes hold either way. The behavioural findings on this page — the empty-content trap, the parameters that don't validate — are properties of the model and apply wherever K3 is served.

{
  "error": {
    "message": "Invalid Authentication",
    "type": "invalid_authentication_error"
  }
}

The errors, by symptom

Reproduced 2026-07-16 against kimi-k3.
SymptomWhat you actually getCause & fix
Empty content,
billed anyway

most likely
HTTP 200
finish_reason: "length"
content: ""
populated reasoning_content
Reasoning consumed the whole token budget. Raise max_completion_tokens or leave it at the 131,072 default. The six runs →
401
Unauthorized
{"error": {
  "message": "Invalid Authentication",
  "type": "invalid_authentication_error"
}}
Key missing, mistyped, revoked, or the Bearer prefix was dropped. Confirm your shell actually exported the variable. There is no code field to match on.
404
model not found
{"error": {
  "message": "Not found the model kimi-k3-chat
              or Permission denied",
  "type": "resource_not_found_error"
}}
The ID is exactly kimi-k3. Note the message conflates "wrong name" with "no access" — if your spelling is right, this is a permissions or listing problem, not a typo. Check your gateway carries K3.
400
empty messages
{"error": {
  "message": "Invalid request: messages
              must not be empty",
  "type": "invalid_request_error"
}}
Your message array got filtered to nothing upstream — usually a history-trimming bug. Guard before sending.
429
rate limited
{"error": {
  "message": "Rate limit exceeded",
  "type": "...",
  "code": null
}}
Per EvoLink's documented schema — we did not trigger this one.
Too many requests or tokens per minute for your tier. Back off exponentially — 1s, 2s, 4s, with jitter. Serialise your own bursts: a retry storm reads as more load.
Timeout /
hung request
No response for tens of seconds Expected at long context: we measured 52s on a 498k-token prompt. Set client timeouts in minutes, not seconds, and use stream: true.
No usage
while streaming
Stream completes, no usage block Pass stream_options: {"include_usage": true}. Without it you are blind to what you spent — including reasoning tokens.
Insufficient
balance
Provider-dependent Check your dashboard. Long contexts spend fast: one 498k-token call is $1.49 of input alone.

The failures that don't error

This is the category worth internalising. K3 does not validate your parameters — we found two cases where an obviously invalid request returns a clean HTTP 200:

Both returned HTTP 200 with a normal completion. 2026-07-16.
What we sentWhy it should failWhat happened
reasoning_effort: "low" Only max is documented as supported HTTP 200 — silently accepted, no warning
max_completion_tokens: 2000000 Nearly double the 1,048,576 context window HTTP 200 — silently accepted, no warning

The lesson generalises beyond these two: a 200 from K3 does not mean your request was understood as you intended. If you are relying on a parameter to change behaviour, verify the behaviour changed — don't infer it from the absence of an error. Combined with the empty-content trap, the rule is simple: assert on the response, not on the status code.

A pre-ship checklist

  1. model in the response echoes kimi-k3 — no silent gateway substitution.
  2. finish_reason == "stop", not "length".
  3. content is non-empty.
  4. usage.total_tokens is non-zero, and you log reasoning_tokens.
  5. Client timeout in minutes if you touch long context.
  6. Any parameter you rely on has been verified by observed behaviour, not by a 200.

The assertion worth having in code is in the API guide.

Reproduce any of this

Every case here reproduces in seconds with a curl and a key. EvoLink carries kimi-k3 on an OpenAI-compatible endpoint — 10 free credits, sign up from anywhere — no Chinese phone number.

Get an EvoLink API key