Home → Help

400 when forcing a tool call on a reasoning model

Set tool_choice to auto and say in the prompt when the tool applies. On models where reasoning is always on, a forced tool call would have to skip that reasoning, so the API refuses the request rather than quietly degrading the model.

What you are seeing

Why it happens

Forcing a tool call is a reasonable thing to want. It is how many frameworks guarantee structured output: define one tool, force it, parse the arguments. That trick stops working on models that reason before every response, because the forced path bypasses the step you are paying for.

The refusal is better than the alternative. An API that accepted the parameter and ignored it would leave you believing you had constrained the model when you had not — a bug that surfaces much later, in production, as occasional unparseable output.

The same models usually reject non-default temperature, top_p and top_k, and do not support prefilling the assistant turn. Code ported from an older model in the same family hits several of these at once, which makes it look like an authentication or endpoint problem rather than a parameter one.

Confirm it is this

Send the same request twice, changing only tool_choice:

for CHOICE in '{"type":"auto"}' '{"type":"any"}'; do
  echo -n "$CHOICE -> "
  curl -s -o /dev/null -w '%{http_code}\n' \
    'YOUR_BASE_URL/chat/completions' \
    -H 'Authorization: Bearer YOUR_KEY' \
    -H 'Content-Type: application/json' \
    -d "{\"model\":\"YOUR_MODEL\",\"max_tokens\":16,
         \"messages\":[{\"role\":\"user\",\"content\":\"hi\"}],
         \"tool_choice\":$CHOICE}"
done

If auto returns 200 and any returns 400, the model does not support forced tool use. Nothing about your key or endpoint is wrong.

How to fix it

  1. Keep tool_choice on auto and be explicit in the promptState the condition plainly — “call search when the question needs current information”. Reasoning models follow that instruction well; it is what they are for.
  2. Use a schema-constrained response instead of a forced toolIf you forced a tool only to get structured output, a structured output or JSON schema parameter does that job directly and is usually still supported.
  3. Check temperature at the same timeFrameworks set a default temperature you never chose. On these models any non-default value is a 400, and it looks identical to the tool_choice failure.
  4. Validate the output rather than trusting the constraintEven where forcing works, models emit arguments that do not match the schema. Parsing with a fallback path is worth writing once.
On APICLAN this applies to claude-fable-5-1 and claude-fable-5: forced tool use, non-default sampling parameters and assistant prefill are all rejected. The Fable 5.1 guide has the full list alongside its cache pricing.

Related

Unexpected token '<' when calling an OpenAI-compatible API401 invalid API key — when the key looks right but still fails

Last checked 2026-10-01. Written from problems diagnosed on a live OpenAI-compatible gateway, not collected from other sites.