Skip to content

Errors reference: M200 documented as HTTP 429 but the platform returns HTTP 200 (friendly message) #52

Description

@SebConejo

Finding

The errors reference describes M200 with the wrong HTTP status.

What the docs say

  • errors.mdx (Limits section): "M200-M203 surface as HTTP 429."
  • errors/M200.mdx (description): "Returns HTTP 429."

What the platform actually does
A request blocked by a usage-limit rule never gets a 429. enforceLimits returns the M200 text through buildFriendlyResponse, which is always an HTTP 200 chat completion with the error as an assistant message — for every client, SDK callers included (packages/backend/src/routing/proxy/proxy.service.ts:260, proxy-friendly-response.ts).

Live reproduction (local Manifest from main, block rule with threshold $0 on the seeded agent):

POST /v1/chat/completions  →  HTTP 200
{"object":"chat.completion","model":"manifest","choices":[{"message":{"role":"assistant",
"content":"[🦚 Manifest M200] You hit your cost limit: $9.29 used, $0.00/month allowed. ..."},
"finish_reason":"stop"}]}

By contrast M201 (rate limit) behaves exactly as documented — a real 429 was reproduced after exceeding 200 req/min:

POST /v1/chat/completions  →  HTTP 429
{"error":{"message":"[🦚 Manifest M201] Too many requests — wait a few seconds and retry. ..."}}

Why it matters: a developer following the docs writes if (status === 429) to detect their spending limits and never sees M200 fire — the response looks like a successful completion.

Secondary findings (same pages)

  1. M200.mdx says the limit metric can be "cost, token, or message-count"; errors.mdx says "Cost or message-count limit". The platform metrics are cost and tokens only (limit-check.service.ts). The Observability page already states it correctly ("Tokens or cost").
  2. errors.mdx says M302 and M303 "both come back as a friendly proxy message". True for chat clients, but M303 is a real HTTP 400 for SDK/tool callers, while M302 is always a 200 friendly message.

Options

A — Fix the docs (smallest change). Rewrite the M200 status wording: M200 is returned as an HTTP 200 chat completion carrying the [🦚 Manifest M200] text as an assistant message; only M201–M203 are HTTP 429. Drop "message-count" from the metric list. Add the M303-is-400-for-SDKs nuance.

B — Align the platform instead. Give M200 the same dual behavior as M204: friendly 200 for chat clients, real 4xx (429) for SDK/tool callers, then keep the docs as written. This is a product decision for mnfst/manifest, not a docs edit — if preferred, this issue should move there and the docs gain the "chat vs SDK" distinction once shipped.

Option A documents today's behavior and can ship immediately; B is arguably the better developer experience but changes the proxy contract.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions