{"error":"missing request body"} with HTTP 400 is Ollama telling you the POST to /api/generate or /api/chat arrived with nothing in it. Send a JSON body: in the test model plus prompt was enough for /api/generate, and model plus messages for /api/chat; when you send it from a file, check that the file is not empty.

In the test an empty file passed with -d "@empty.json", a bare -X POST, and a POST with only a Content-Type header all got this answer. The same request with a filled file returned HTTP 200.

The error

PS> (Get-Item empty.json).Length
0
PS> curl.exe -s -w "\nHTTP %{http_code}\n" http://127.0.0.1:15190/api/generate -d "@empty.json"
{"error":"missing request body"}
HTTP 400
PS> curl.exe -s -w "\nHTTP %{http_code}\n" -X POST http://127.0.0.1:15190/api/chat
{"error":"missing request body"}
HTTP 400
PS> curl.exe -s -w "\nHTTP %{http_code}\n" -X POST -H "Content-Type: application/json" http://127.0.0.1:15190/api/generate
{"error":"missing request body"}
HTTP 400

Why it happens

The answer does not name a field such as model, because there was no JSON at all to look into. A header alone does not count, as the third command shows. In this test the empty body came from two places: a file that was created but never filled, the zero-byte empty.json, and a -X POST written without -d.

Two neighbouring cases give different answers, which helps to tell them apart. A file name that does not exist never reaches Ollama: curl stops with curl: option -d: error encountered when reading a file. A body that starts but is cut off before its closing brace does reach it, and comes back as {"error":"unexpected EOF"}, also with 400.

The fix

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

Put the JSON in an ASCII file and pass it with -d "@file"; check the size with (Get-Item file).Length if the answer is still the same. In code, make sure the request content is set before the POST is sent, not only its headers.

How it was reproduced

On Windows 11 in Windows PowerShell 5.1 with curl 8.22.0 and 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. A zero-byte file was written with [IO.File]::WriteAllText, then sent; a bare POST and a POST with only a header followed; then a file name that does not exist, a body without its closing brace, and the complete body.

Frequently asked

Why does the Ollama API say missing request body?
The POST arrived without any body. In the test an empty file sent with curl -d, a bare -X POST, and a POST with only a Content-Type header all got HTTP 400 with this message.
What is the difference between missing request body and unexpected EOF in Ollama?
missing request body means nothing was sent. unexpected EOF means a body was sent but the JSON stopped early, for example without its closing brace. Both come back with HTTP 400.
What does curl option -d error encountered when reading a file mean?
In the test the file named after the @ sign did not exist, so curl stopped before sending anything. Check the file name and the current folder.

More decoded errors in the Fixes category. For quoting JSON on the PowerShell command line, see the curl quoting post.