MiniMax-AI / MiniMax-AI/minimax-code

`tools: []` sent on automatic-compaction requests → HTTP 400 from OpenAI-compatible backends (vLLM); compaction never succeeds

Open
#194 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug cli
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 against main @ 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

  1. The compaction request is built without a tools field:
    packages/local-runtime-v2/src/service/turn-system/compaction/execution/checkpoint-provider.ts L174–187, buildCheckpointRequest() returns { systemPrompt, messages: [...projected, control] }. The projected messages still contain the assistant tool calls / tool results of the history.

  2. 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.ts L548–550

    } else if (hasToolHistory(context.messages)) {
        // Anthropic (via LiteLLM/proxy) requires tools param when conversation has tool_calls/tool_results
        params.tools = [];
    }
    

    (hasToolHistory is 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-tools case in #3649 / #3650 but deliberately kept this hasToolHistory branch, so it is still present in the vendored v0.79.1 and on upstream main.

The combination (1) + (2) means every compaction call against an OpenAI-compatible backend carries "tools": [].

Reproduction

  1. mcode provider add --name local --base-url http://<vllm-or-proxy>/v1 --api-format openai-completions --model <model> --api-key-env KEY --use
  2. 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).
  3. 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's openai-completions-empty-tools.test.ts covers 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 receives tools in 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.