Skip to content

Provider API: generic 400 invalid_request_error (trace_id only) for long tool-heavy requests, far below the advertised 1M context #976

Open

Description

@depler

Summary

The Provider API returns a generic 400 invalid_request_error (body carries only a trace_id, no code/param) for a long, tool-heavy OpenAI-compatible request on deepseek/deepseek-v4.1-flash, even though /provider/v1/models advertises context_length: 1000000 and the request is well under that. Once a session reaches this state, every subsequent request with the same history is rejected; a fresh session on the same key/model works.

Environment

  • Endpoint: POST https://api.commandcode.ai/provider/v1/chat/completions
  • Model: deepseek/deepseek-v4.1-flash (also seen on the same class of requests with deepseek/deepseek-v4-flash)
  • Client: OpenCode v2 + opencode-cmd-provider (OpenAI Chat Completions, stream: true, max_tokens: 64000, stream_options.include_usage: true, reasoning_effort set)
  • Date observed: 2026-10-02

Actual behavior

Command Code API error 400:
{"message":"invalid request error trace_id: <hex>","type":"invalid_request_error"}

The envelope has no code, no param, and no detail — only the trace_id. The same request shape is served with 200 at a slightly smaller size.

Evidence

Raw replays against /provider/v1/chat/completions, same model, same key:

Request (tool-heavy history from the failing session) prompt_tokens Result
history as-is 248,690 200
+ benign system block 276,250 200
+ benign system block 278,091 / 279,491 200
+ benign system block ~281,600 400
+ benign system block ~286,000 400
+ benign system block ~288,000 400
+ benign system block ~294,000 400
  • The added block is plain English ("You are a helpful coding agent. " repeated) — meaningless on its own, so this is not the added content being rejected; identical content passes below the boundary.
  • Near the boundary (~278k–281k) the same byte-identical body flips between 200 and 400 across consecutive calls (observed repeatedly), which points at inconsistent per-upstream limits behind the gateway rather than a deterministic schema rejection.
  • A plain single-user-message request at 290,033 tokens returns 200, so the effective ceiling is not a simple global token cap — it depends on the request's shape/composition.

Representative trace_ids (happy to provide more / the exact bodies):

d3ecfed848e2f9238dcf7df9976151a1   523969ce0769307c4b53a71cd97ba656
c8e65493eb3fc2ee14e67193447bf656   f45d648d521d91dae2fcb830bb1153d7
8cb9128b60428f59c5b57e69fb0baa8d   652bcca23eb260b5461ad1f6dfc70ca9
ff8a514d7208bd743b11e17fb05390f1   ba4cd8f9dbcb28c327e6218ccc1b6243
e3d8bc71d74eaa9a8f882130c5d6f17a   01c842f26a29e9265244ad35ed4df539
a980384c83edd0e23b70f28ae08978a2   07603ff4e5721b6bd2607edb84ff4378
5794a8bf53e87083bd6a5fd908d0bdd8   1db8813539d7ac76d68193b896e5b2c7
2f6228764d00865cf6abce943439e7e9   26bd02da3c32500d8e829ee648a8b24c

Expected behavior

Either serve the request (it is far below the advertised 1M window), or return a diagnosable error — e.g. 413 / context_length_exceeded naming the actual limit and the current input size — instead of a generic 400 invalid_request_error with only a trace_id. The advertised context_length and the effectively served limit should agree.

Impact

  • A single long tool-heavy session becomes permanently unusable — every request replays the same history and gets the same 400; retries (the client retries 2–4×) all fail identically.
  • The only recovery is starting a new session and losing all context.
  • Because the error is generic, it is impossible to distinguish "your prompt exceeded a limit" from "your request body is malformed", and the client cannot auto-compact intelligently.

Notes

  • I saw your Reddit reply on the "Deepseek V4 Pro Context Length Capped?" thread asking for /trace details — these trace_ids are from the OpenAI-compatible Provider API (not the CLI), so hopefully they are queryable server-side.
  • I'm happy to share the exact request body (≈1 MB) privately if it helps, or to build a client-side repro with you.

Thanks!

Reproduction files

Two request bodies (secret gist): https://gist.github.com/depler/5c153c6a9e4adac2e63a9d538f0f78b2

  • control_200.json — the session's outbound body (134 messages) → HTTP 200, prompt_tokens=248690
  • failing_400.json — the same body plus one benign system message prepended → HTTP 400
    (trace_id: 65a715e992e7b714bfc7eae1f34e978a)
  • README.md — the exact curl commands and observed results

The only difference between the two files is the extra system message at
messages[0]; the flip to 400 is caused by the resulting request size/shape,
not by the added text.

Activity

  1. depler commented on Oct 2, 2026

    @depler
    Author

    Added the two request bodies as files (secret gist): https://gist.github.com/depler/5c153c6a9e4adac2e63a9d538f0f78b2

    • control_200.json → HTTP 200 (prompt_tokens=248690)
    • failing_400.json → HTTP 400 (trace_id: 65a715e992e7b714bfc7eae1f34e978a) — byte-identical to the control except for ONE extra benign system message at messages[0]
    • README.md → curl commands + observed results

    So two copy-pasteable examples: same conversation, one returns 200, the other 400.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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