MiniMax-AI / MiniMax-AI/minimax-code
`tools: []` sent on automatic-compaction requests → HTTP 400 from OpenAI-compatible backends (vLLM); compaction never succeeds
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 1.3k
- Forks
- 141
- Avg merge
- 2h 45m
- Merged PRs (30d)
- 46
Description
Summary
When a session is run against an OpenAI-compatible backend (--api-format openai-completions) and the conversation contains tool calls, the automatic context compaction request is sent with "tools": []. vLLM rejects that with HTTP 400, so compaction never succeeds and is re-attempted on every following agent step. The main agent requests (which carry the real tool list) keep working, so the failure is easy to miss — but the context is never compacted and keeps growing until the model's window is exhausted.
Environment
- minimax-code:
0.4.12(code references below are againstmain@ 81b666a, 2026-09-19) - Provider: custom,
--api-format openai-completions - Backend: LiteLLM proxy (v1.101.0) in front of vLLM (OpenAI-compatible
/v1/chat/completions)
Observed
Error returned to the client:
400 - {"error":{"message":"`tools` must not be an empty array. Either provide at least one tool or omit the field entirely. (parameter=tools)","type":"BadRequestError","param":"tools","code":400}}
Request pattern once the context passes the compaction threshold: every successful main request (with tools) is accompanied by failing requests carrying roughly the same conversation minus the tool definitions (~30k tokens smaller) — i.e. the compaction checkpoint call. In one session this produced 41 failed requests in 4 minutes while the main turns continued normally. The compaction never completes.
Note that this is not vLLM being strict for its own sake: the OpenAI API itself rejects "tools": [] as well, so any provider that mirrors OpenAI validation will fail here.
Root cause
-
The compaction request is built without a
toolsfield:
packages/local-runtime-v2/src/service/turn-system/compaction/execution/checkpoint-provider.tsL174–187,buildCheckpointRequest()returns{ systemPrompt, messages: [...projected, control] }. The projected messages still contain the assistant tool calls / tool results of the history. -
The vendored pi-ai provider then injects an empty array whenever the history has tool calls but no tools are supplied:
third_party/pi-mono/packages/ai/src/providers/openai-completions.tsL548–550} else if (hasToolHistory(context.messages)) { // Anthropic (via LiteLLM/proxy) requires tools param when conversation has tool_calls/tool_results params.tools = []; }(
hasToolHistoryis at L48.) This branch is a workaround for Anthropic-behind-a-proxy that came in upstream via earendil-works/pi #149 / #150. Upstream later fixed the--no-toolscase in #3649 / #3650 but deliberately kept thishasToolHistorybranch, so it is still present in the vendored v0.79.1 and on upstreammain.
The combination (1) + (2) means every compaction call against an OpenAI-compatible backend carries "tools": [].
Reproduction
mcode provider add --name local --base-url http://<vllm-or-proxy>/v1 --api-format openai-completions --model <model> --api-key-env KEY --use- Work in a session that uses tools until the context reaches the automatic-compaction trigger (a few hundred k tokens of tool output gets there quickly).
- Watch the backend log: each agent step now logs one 200 (main request) and a 400 with the message above (compaction request).
Minimal backend-side check, independent of mcode:
curl -s http://<vllm>/v1/chat/completions -H 'Content-Type: application/json' \
-d '{"model":"<model>","messages":[{"role":"user","content":"hi"}],"tools":[]}'
# -> 400 "`tools` must not be an empty array"
Suggested fix
Any of these would do; (a) is the smallest and matches the repo's vendor-patch workflow (third_party/pi-mono/MINIMAX_CHANGES.md):
- (a) In the vendored provider, drop the
else if (hasToolHistory(...))branch, or gate it so it only fires for Anthropic-style compat (e.g.compat.cacheControlFormat === "anthropic"). Upstream'sopenai-completions-empty-tools.test.tscovers the surrounding cases and would need its third case inverted. - (b) In
transformLocalTurnPayload(local-turn-payload-transform.ts), which already runs for both main and compaction requests:if (Array.isArray(payload.tools) && payload.tools.length === 0) delete payload.tools; - (c) Pass the tool list through in
buildCheckpointRequest— the compactor already receivestoolsin its input — though (a)/(b) are cleaner since the checkpoint call is not supposed to call tools.
Workaround
--api-format anthropic-messages or --api-format openai-responses: both provider paths only set tools when the list is non-empty, so the compaction request goes through.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with packages/local-runtime-v2/src/service/turn-system/compaction/execution/checkpoint-provider.ts and trace buildCheckpointRequest(), then inspect third_party/pi-mono/packages/ai/src/providers/openai-completions.ts and its hasToolHistory branch. Run the referenced openai-completions-empty-tools.test.ts and verify automatic compaction requests no longer send tools: [] to OpenAI-compatible backends while existing tool-history behavior remains covered.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100