{"error":"option \"num_ctx\" must be of type integer"} is Ollama's answer, with HTTP 500, when a value inside the request's options object is a JSON string instead of a number. Remove the quotes around the number: "num_ctx": 4096, not "num_ctx": "4096".

The same rule holds for decimal options: "temperature": "0.2" gave must be of type float32. With both values written as bare numbers the request returned HTTP 200 and an answer.

The error

{"model":"qwen2.5:0.5b","prompt":"Say hi","stream":false,"options":{"num_ctx":"4096"}}
PS> curl.exe -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:15190/api/generate -d "@ctx.json"
{"error":"option \"num_ctx\" must be of type integer"}
HTTP 500
PS> curl.exe -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:15190/api/generate -d "@temp.json"
{"error":"option \"temperature\" must be of type float32"}
HTTP 500
PS> curl.exe -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:15190/api/chat -d "@chat-ctx.json"
{"error":"option \"num_ctx\" must be of type integer"}
HTTP 500

Why it happens

options carries the model parameters (num_ctx, temperature and the rest), and each one has a type. A string with digits in it is still a string in JSON, so "4096" is refused and the message names the option and the type it wanted. /api/generate and /api/chat gave the same words. The reply held only the error field, with no response, message or timing fields, so no answer came with it, and the corrected body sent to the same server a moment later was answered normally. The status is 500, not 400, so client code that treats 5xx as "server down, retry later" will retry a request that can never succeed.

A top-level field with the wrong type gives a different message and a 400: "stream":"false" produced json: cannot unmarshal string into Go struct field .GenerateRequest.stream of type bool. The fix there is the same: false without quotes.

The fix

{"model":"qwen2.5:0.5b","prompt":"Say hi","stream":false,"options":{"num_ctx":4096,"temperature":0.2}}
PS> curl.exe -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:15190/api/generate -d "@ctx-ok.json"
{"model":"qwen2.5:0.5b",...,"response":"Hello! How can I help you today?","done":true,"done_reason":"stop",...
HTTP 200

Write numbers bare and booleans as true or false. When the body is built in code, convert the configuration value to a number before you serialize it, so the JSON library writes it without quotes.

How it was reproduced

On Windows 11 with Ollama 0.40.16, against a separate test server on 127.0.0.1:15190 with its own models folder and qwen2.5:0.5b. The JSON bodies were written to ASCII files with [IO.File]::WriteAllText and sent with curl.exe -d "@file" from Windows PowerShell 5.1: a quoted num_ctx to /api/generate and /api/chat, a quoted temperature, a quoted stream, and then the corrected body.

Frequently asked

Why does Ollama say option num_ctx must be of type integer?
The num_ctx value in the options object was sent as a JSON string such as "4096". Send it as a bare number, 4096, and the request is accepted.
What does 'must be of type float32' mean in the Ollama API?
A decimal option such as temperature was sent as a string. Write it as a number, for example 0.2 without quotes.
Why does Ollama return HTTP 500 for a bad option?
In the test with Ollama 0.40.16 a string in options came back with status 500, while a wrong type in a top-level field such as stream came back with 400. Both are fixed in the request body, so do not retry them unchanged.

More decoded errors in the Fixes category. For a messages field of the wrong shape, see Ollama API: 'model not found', 'model is required' and 'cannot unmarshal'.