The auxiliary.title_generation 400: Why My Agent Warned Me on Every New Session

ai hermes agents configuration api

Every new command-line session started the same way this week, with a line I could not explain:

⚠ Auxiliary title generation failed: HTTP 400: This model always engages in
thinking and cannot be disabled; please use low, high, or max

It appeared once per session, usually a second after my first message. Nothing I asked for failed. No answer was cut short. The agent kept working — and still the warning came back on the next session, and the next.

This is the trace of what was actually happening, the config line responsible, and the fix. It took one line to fix and about twenty minutes to prove, and most of that was the step I would have skipped a year ago: asking the provider directly instead of reasoning about it.

What the warning was really about

Nothing in the main conversation. "Auxiliary" is the collective name for an agent's side-calls: the small, cheap model invocations a harness makes for housekeeping — summarising a long conversation, describing an image, naming a session. The warning came from the naming one.

When a session gets its name, two things happen. First an instant, deterministic title is written from the first line of what I typed — that never fails, because it involves no model at all. Then a background thread asks a small model for a better, 3-to-7-word version. That second call is what was failing, and its failure was being reported to me in the chat as a status line.

The blast radius, measured honestly: the session kept the instant title, and only the polished upgrade was lost. No turn was damaged, no context was lost, nothing was silently half-done. The warning was real, and it was cosmetic.

Step one: which model was it even calling?

My config did not pin a model for that task, and an unpinned auxiliary task means auto — and auto means the session's main model. One grep on the agent log showed the whole route:

Auxiliary auto-detect: using main provider zai (glm-5.3-flash)
Auxiliary title_generation: using zai (glm-5.3-flash) at https://api.z.ai/api/paas/v4/
Title generation failed: Error code: 400 - {'error': {'code': '1210', 'message':
  'This model always engages in thinking and cannot be disabled; please use low, high, or max'}}

Three lines, no speculation required. My sessions were running on a GLM-5.3-Flash main model, so the naming call inherited it. And the crucial detail: the main conversation on that same model worked perfectly. Whatever was wrong was in how the side-call was shaped, not in the model being unavailable — which ruled out keys, quota, and outages before I spent a minute on them.

Step two: read the call

The title generator asks for thinking off, deliberately. A session title is a machine-readable JSON answer capped at 64 tokens; on a thinking model the internal reasoning tokens would eat that budget before the JSON ever landed, and an earlier bug had already stored a stray ```json fence as a session name because of exactly that.

So the call says: no thinking. And the provider profile — the small piece of code that translates a harness's generic "reasoning off" into a specific vendor's dialect — turned that into extra_body.thinking = {"type": "disabled"} for any GLM 4.5 or newer.

The provider did not agree. For the GLM-5.3 family, thinking is not a switch. It is an effort level: low, high, or max. Asking for it to be off is not a valid request, and the API says so in the clearest possible terms — its own error message tells you which values it accepts.

Step three: ask the API instead of guessing

I stopped reading code at that point and sent five tiny requests straight to the provider (a handful of tokens each, a fraction of a cent) to see which combinations are actually accepted:

  • glm-5.3-flash, thinking disabled → 400, code 1210
  • glm-5.3-flash, thinking enabled → 200
  • glm-5.3-flash, reasoning_effort: low → 200
  • glm-4.5-flash, thinking disabled → 200
  • deepseek-flash, thinking disabled → 200

That single experiment did three jobs at once. It reproduced the error on demand, it proved the model itself was fine (two of the three GLM requests succeeded), and it produced a list of candidate targets for the fix instead of a hunch. The wider catalogue agrees: glm-5.3-flash advertises reasoning as an effort-only option, while glm-4.5-flash advertises a plain toggle.

The fix: stop letting a side-call inherit a session setting

The title task does not need my session's model. It needs any model that will honour "no thinking". So I pinned it:

hermes config set auxiliary.title_generation.provider zai
hermes config set auxiliary.title_generation.model glm-4.5-flash

An explicit per-task provider is resolved before any auto-detection, so this is deterministic — no launch flag can re-break it. I confirmed it through the real code path rather than a config dump, by calling the production title function with the same input that had failed three times:

Auxiliary title_generation: using zai (glm-4.5-flash) at https://api.z.ai/api/paas/v4/
POST https://api.z.ai/api/paas/v4/chat/completions "HTTP/1.1 200 OK"
TITLE: 'Explain percentile calculation block'

One request, one 200, a sensible title, no retry. (deepseek-flash also passes the thinking-off test, but it rejects the JSON-schema response format and needs a second attempt without it — a wasted round trip on every session, so it lost.) If you would rather not make the call at all, auxiliary.title_generation.enabled false keeps the deterministic titles and spends nothing.

The part worth keeping

Two things generalise beyond this bug.

First: "auto" is an inheritance rule, not a safety net. Every auxiliary task that is left unpinned follows whatever model that session happens to run on — including a model you picked for one session with a launch flag. The moment a provider ships a family where one member has different capabilities than its siblings, that inheritance carries the difference straight into side-calls you never think about. Pinning the cheap, latency-critical ones costs one line each and removes an entire class of surprise.

Second: an HTTP error with a provider's own JSON body is not a routing failure — it is the model rejecting the shape of your request. That distinction decides where you look. And the cheapest possible experiment sits right there: five requests, a few seconds, one provider's own words. I had the answer before I had a theory.

There is an upstream wrinkle worth naming: the provider profile currently treats the whole GLM 4.5-and-later family as thinking-toggleable, and the 5.3 family is effort-only. Anything asking that family to turn thinking off will get the same 400 — the config pin is the workaround, not the cure.

The warning was harmless. The habit of verifying it was not.