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

F

o

r

c

i

n

g

a

t

o

o

l

c

a

l

l

i

s

a

r

e

a

s

o

n

a

b

l

e

t

h

i

n

g

t

o

w

a

n

t

.

I

t

i

s

h

o

w

m

a

n

y

f

r

a

m

e

w

o

r

k

s

g

u

a

r

a

n

t

e

e

s

t

r

u

c

t

u

r

e

d

o

u

t

p

u

t

:

d

e

f

i

n

e

o

n

e

t

o

o

l

,

f

o

r

c

e

i

t

,

p

a

r

s

e

t

h

e

a

r

g

u

m

e

n

t

s

.

T

h

a

t

t

r

i

c

k

s

t

o

p

s

w

o

r

k

i

n

g

o

n

m

o

d

e

l

s

t

h

a

t

r

e

a

s

o

n

b

e

f

o

r

e

e

v

e

r

y

r

e

s

p

o

n

s

e

,

b

e

c

a

u

s

e

t

h

e

f

o

r

c

e

d

p

a

t

h

b

y

p

a

s

s

e

s

t

h

e

s

t

e

p

y

o

u

a

r

e

p

a

y

i

n

g

f

o

r

.

T

h

e

r

e

f

u

s

a

l

i

s

b

e

t

t

e

r

t

h

a

n

t

h

e

a

l

t

e

r

n

a

t

i

v

e

.

A

n

A

P

I

t

h

a

t

a

c

c

e

p

t

e

d

t

h

e

p

a

r

a

m

e

t

e

r

a

n

d

i

g

n

o

r

e

d

i

t

w

o

u

l

d

l

e

a

v

e

y

o

u

b

e

l

i

e

v

i

n

g

y

o

u

h

a

d

c

o

n

s

t

r

a

i

n

e

d

t

h

e

m

o

d

e

l

w

h

e

n

y

o

u

h

a

d

n

o

t

a

b

u

g

t

h

a

t

s

u

r

f

a

c

e

s

m

u

c

h

l

a

t

e

r

,

i

n

p

r

o

d

u

c

t

i

o

n

,

a

s

o

c

c

a

s

i

o

n

a

l

u

n

p

a

r

s

e

a

b

l

e

o

u

t

p

u

t

.

T

h

e

s

a

m

e

m

o

d

e

l

s

u

s

u

a

l

l

y

r

e

j

e

c

t

n

o

n

-

d

e

f

a

u

l

t

<

c

o

d

e

>

t

e

m

p

e

r

a

t

u

r

e

<

/

c

o

d

e

>

,

<

c

o

d

e

>

t

o

p

_

p

<

/

c

o

d

e

>

a

n

d

<

c

o

d

e

>

t

o

p

_

k

<

/

c

o

d

e

>

,

a

n

d

d

o

n

o

t

s

u

p

p

o

r

t

p

r

e

f

i

l

l

i

n

g

t

h

e

a

s

s

i

s

t

a

n

t

t

u

r

n

.

C

o

d

e

p

o

r

t

e

d

f

r

o

m

a

n

o

l

d

e

r

m

o

d

e

l

i

n

t

h

e

s

a

m

e

f

a

m

i

l

y

h

i

t

s

s

e

v

e

r

a

l

o

f

t

h

e

s

e

a

t

o

n

c

e

,

w

h

i

c

h

m

a

k

e

s

i

t

l

o

o

k

l

i

k

e

a

n

a

u

t

h

e

n

t

i

c

a

t

i

o

n

o

r

e

n

d

p

o

i

n

t

p

r

o

b

l

e

m

r

a

t

h

e

r

t

h

a

n

a

p

a

r

a

m

e

t

e

r

o

n

e

.

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-09-09. Written from problems diagnosed on a live OpenAI-compatible gateway, not collected from other sites.