SSyntha
llm-costs13 Sept 2026·2 min read

Why GLM-5.3-flash returns empty content, and the one parameter that fixes it

The endpoint has mandatory reasoning. With default settings it spends the entire max_tokens budget on reasoning and returns an empty message — and both obvious ways to disable it return a 400.

Set reasoning: { effort: "minimal" }. That is the fix. Everything below is why the obvious alternatives fail, because we lost a batch to this and the failure mode gives you nothing to work with.

The symptom

You point a working generation pipeline at z-ai/glm-5.3-flash, change nothing else, and every row fails. Not with an error — with an empty content string. The request returns 200. Token usage looks normal, sometimes high. The message is blank.

In our case: 0 of 8 rows produced usable output, with no error to trace.

The cause

This endpoint has mandatory reasoning. With default settings the model spends its entire max_tokens allowance on reasoning tokens and has nothing left for the visible message. At max_tokens: 4000, all 4000 go to reasoning.

Because reasoning tokens are billed and counted but not returned in content, the usage numbers look like the model worked. It did. You just cannot see any of it.

Why you cannot simply turn it off

Both intuitive approaches fail with a 400:

Attempt Result
reasoning: { enabled: false } 400 — "Reasoning is mandatory for this endpoint"
reasoning: { max_tokens: 0 } 400 — same
reasoning: { effort: "minimal" } Works. Reasoning drops to roughly zero tokens.
No reasoning parameter at all Empty content — the default burns the budget

That last row is the dangerous one. Omitting the parameter is not neutral. A pipeline that works against every other model will fail against this one without any code change, purely by switching a model name in configuration.

Why it is worth the trouble anyway

Once configured, this model was the best value we measured for Arabic conversational generation:

Model Pass rate against the same gate Cost per row 50,000 rows
z-ai/glm-5.3-flash + effort: "minimal" 59/62 (95%) ~$0.00076 ~$34–46
deepseek/deepseek-v4-flash 20/27 (74%) ~$0.0016–0.0027 ~$80–133

Same prompts, same validators, same corpus. The pass-rate difference matters more than the token price, because a failed row costs a full retry.

The general lesson for model swapping

If your pipeline stores the model name as runtime configuration — which it should — then a model name is not a safely swappable value. Providers differ in required parameters, and the failures are not always loud.

Two defences worth building:

Assert on non-empty content. Treat an empty content as a hard error at the client boundary, not as a row that failed validation. Those are different bugs and conflating them cost us a batch of confused debugging.

Log reasoning token counts separately when the provider reports them. A row with 4,000 reasoning tokens and 0 completion tokens is instantly diagnosable; a row with "4,000 tokens used" is not.

Checklist before switching generation models

  • Does the endpoint require or default to reasoning?
  • What happens to max_tokens when it does?
  • Does an empty content surface as an error in your client?
  • Have you re-measured pass rate, not just token price?

The last point is the one that decides your bill. Token price is what you compare on a pricing page; pass rate is what you pay.

Syntha · SynthaAll notes