System.Text.Json.JsonException: '{' is invalid after a single JSON value. Expected end of data.
— your .NET code read an Ollama reply as one JSON document, but Ollama streamed it as many
JSON objects, one per line. Add stream = false to the request body, or read the
reply line by line.
Both /api/generate and /api/chat streamed in the test when the
request body had no stream field: the reply was newline-delimited JSON
(84 lines from /api/chat for one short answer), and
ReadFromJsonAsync threw this same exception for both endpoints. Setting
stream = false gives a single object that deserializes normally.
The error
PS> dotnet run --no-build -- broken
Unhandled exception. System.Text.Json.JsonException: '{' is invalid after a single JSON value. Expected end of data. Path: $ | LineNumber: 1 | BytePositionInLine: 0.
---> System.Text.Json.JsonReaderException: '{' is invalid after a single JSON value. Expected end of data. LineNumber: 1 | BytePositionInLine: 0.
at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
...
at System.Net.Http.Json.HttpContentJsonExtensions.ReadFromJsonAsyncCore[T](HttpContent content, JsonSerializerOptions options, CancellationToken cancellationToken)
at Program.<Main>$(String[] args) in ...\StreamLab\Program.cs:line 12
at Program.<Main>(String[] args)
exit code: -532462766
The middle of the stack trace (the "..." line) and the start of the path are shortened;
nothing else changed. Line 12 is the ReadFromJsonAsync call:
var body = new { model = "qwen2.5:0.5b", prompt = "Name three primary colors." };
using var response = await http.PostAsJsonAsync("/api/generate", body);
response.EnsureSuccessStatusCode();
var reply = await response.Content.ReadFromJsonAsync<GenerateReply>();
Why it happens
With streaming on, the body is a sequence of JSON objects, one per line; the
lines version of the test app read it exactly that way, deserializing each
line on its own until one had done set to true. Read as one document, the
body fails where the message says: another { at
LineNumber: 1 | BytePositionInLine: 0, the start of a line after the first
object, where System.Text.Json expected the end of the data.
The HTTP status is 200, so EnsureSuccessStatusCode passes and the failure only
appears at deserialization. A sample written against a body with
"stream": false, or tested with that flag in curl, breaks as soon as the flag
is left out.
The fix
var body = new { model = "qwen2.5:0.5b", prompt = "Name three primary colors.", stream = false };
using var response = await http.PostAsJsonAsync("/api/generate", body);
response.EnsureSuccessStatusCode();
var reply = await response.Content.ReadFromJsonAsync<GenerateReply>();
Console.WriteLine(reply!.response);
PS> dotnet run --no-build -- fixed
Three primary colors are red, yellow, and blue.
exit code: 0
If you want the text as it is generated, keep streaming and read one line at a time instead. This version ran in the same app and printed the same answer:
using var request = new HttpRequestMessage(HttpMethod.Post, "/api/generate") { Content = JsonContent.Create(body) };
using var response = await http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
response.EnsureSuccessStatusCode();
using var reader = new StreamReader(await response.Content.ReadAsStreamAsync());
while (await reader.ReadLineAsync() is { } line)
{
if (line.Length == 0) continue;
var part = JsonSerializer.Deserialize<GenerateReply>(line)!;
Console.Write(part.response);
if (part.done) Console.WriteLine();
}
ResponseHeadersRead hands you the body as it arrives instead of after it is
complete. The record used by all three versions is
record GenerateReply(string response, bool done);.
How it was reproduced
A console app from dotnet new console on .NET SDK 10.0.403 (net10.0), no
NuGet packages, only HttpClient, System.Net.Http.Json and
System.Text.Json. It called a separate test server started with
ollama serve (Ollama 0.40.14) on 127.0.0.1:15190 with
qwen2.5:0.5b; the project name StreamLab is the test's own. One
argument picks the version: broken threw, fixed and
lines printed the answer.
Frequently asked
- Why does ReadFromJsonAsync fail on an Ollama response?
- Ollama streams /api/generate and /api/chat by default, one JSON object per line. ReadFromJsonAsync expects a single JSON value and throws at the second line. Send stream false or read line by line.
- How do I turn off streaming in the Ollama API from C#?
- Add stream = false to the request body object, for example new { model, prompt, stream = false }. The reply is then one JSON object with the whole answer in response.
- How do I read an Ollama stream in .NET?
- Send the request with HttpCompletionOption.ResponseHeadersRead, open the body with ReadAsStreamAsync, wrap it in a StreamReader and deserialize each non-empty line on its own until an object has done set to true.
More decoded errors in the Fixes category. Every Ollama endpoint from curl and .NET, streaming included, is in the Ollama API post.