Rust serde_json: EOF while parsing a value
Fix serde_json "EOF while parsing" errors caused by empty bodies, truncated responses, or deserializing into the wrong shape.
Symptoms
- Error("EOF while parsing a value", line: 1, column: 0)
- Deserialization fails only when the HTTP body is empty
- The error reports a nonzero column implying truncation
Possible causes
- An HTTP body is empty (204 or a failed request) but parsed as JSON
- The response was truncated mid-transfer
- Deserializing a stream with from_reader after the reader was consumed
- The content is not JSON at all (HTML error page)
Diagnosis steps
Log the raw body before deserializing
let body = resp.text().await?; println!("{body}");Interpretation
An empty string or HTML explains column 0 or a mid-value EOF.
Check the HTTP status before parsing
Interpretation
A 204 or 500 body should be handled as an error, not fed to the JSON parser.
Match the deserialization target to the payload shape
Interpretation
A type mismatch elsewhere in the value can also surface as an EOF at the last field.
Root cause
serde_json streams tokens and expects a complete JSON value. An empty or truncated body makes the parser hit end-of-input before finishing a value, producing "EOF while parsing". The exact column distinguishes an empty body (column 0) from truncation deeper in the document.
Fix
Check status and emptiness before parsing: read the body into a String, verify it is non-empty, and only then call serde_json::from_str. For optional bodies use an enum or Option. When truncation is possible, validate Content-Length and retry, and never assume a 200 always carries a parseable JSON body.
Verify the fix
Re-run against the endpoint that returned empty; the code now handles the empty case without a parse error, and a test feeding an empty string asserts a clear application error instead of an EOF panic.
Symptoms
Deserialization fails with EOF while parsing a value at line 1 column 0 or a nonzero column deeper in the stream. It correlates strongly with empty bodies or truncated responses rather than with valid JSON.
Diagnosis
Inspect the raw body and status before parsing:
let status = resp.status();
let body = resp.text().await?;
eprintln!("status={status} body={body:?}");
let value: Value = serde_json::from_str(&body)?;
Column 0 means an empty input; a later column means truncation or a handoff bug.
Fix
Guard empty and non-JSON bodies:
if !status.is_success() {
return Err(format!("http {status}").into());
}
if body.trim().is_empty() {
return Ok(None); // or a defined default
}
let value = serde_json::from_str(&body)?;
Verification
Call the endpoint that used to return an empty body; no EOF error occurs, and a test deserializing “” returns a clear message rather than a serde EOF.
Additional Notes
An end-of-input error during parsing almost always means the bytes are not what the parser expected: empty because a request failed, truncated because a transfer ended early, or not json at all because an error page arrived instead. Checking the status code and trimming the body before parsing converts a confusing parse error into a clear contract violation. Comparing the declared content length to the received size detects truncation, and treating an empty body as a defined case rather than valid json stops the parser from becoming the place where unrelated failures surface.