serde_json: expected value at line 1 column 1
Updated by John Marshall
Rust’s serde_json gives this error when the very first character of the text can’t start a JSON value. JSON has to begin with {, [, a double quote, a number, or true, false or null. Anything else and serde_json gives up at column 1. When your code calls an API, the usual culprit is an HTML page: the server sent back a 404 or a login page, and the code parsed it without looking at the status.
Quick fix
- Read the response body as text and print the start of it. If it begins with
<, it’s a web page. - Check the status code before parsing. Between the status and the page itself, you’ll usually see what went wrong: a wrong URL, a missing token, or a rate limit.
- For any other message, find it in the tables below. The line and column point at the problem.
An example
This code fetches a user and reads the response straight into a struct:
#[derive(Deserialize)]
struct User {
id: u64,
name: String,
}
let user: User = reqwest::get("https://api.example.com/users/42")
.await?
.json()
.await?;If user 42 doesn’t exist, the server answers with a 404 page in HTML. reqwest doesn’t treat a 404 as an error, so it hands the page to serde_json, which stops at the <. If main returns the error through anyhow, the program prints:
Error: error decoding response body
Caused by:
expected value at line 1 column 1If you call .unwrap() on the result of serde_json::from_str instead, the same error is printed in its debug form:
called `Result::unwrap()` on an `Err` value: Error("expected value", line: 1, column: 1)Lines and columns both start at 1, and the column counts bytes rather than characters, so an emoji earlier on the line pushes it along by four. An empty body gives EOF while parsing a value at line 1 column 0 instead, because there was nothing to read.
See what the server actually sent
reqwest makes this harder to track down. Printed the usual way, with {}, its error says only error decoding response body. The serde_json message is underneath, in source(), and {:?} shows it, as does {:#} with anyhow. But neither one shows the body, and the body is what tells you what happened.
Instead of .json(), read the body as text, check the status, and parse it yourself. A failed request then reports what the server actually said:
let resp = reqwest::get(url).await?;
let status = resp.status();
let body = resp.text().await?;
if !status.is_success() {
anyhow::bail!("{status}: {}", body.chars().take(300).collect::<String>());
}
let user: User = serde_json::from_str(&body).map_err(|e| {
anyhow::anyhow!("{e}; body starts with {:?}", body.chars().take(100).collect::<String>())
})?;The status code usually explains it. A 404 means the URL or ID is wrong. A 401 or 403 means the request isn’t authenticated. reqwest follows redirects, so a redirect to a sign-in page arrives as a 200 with HTML in it. A 429 means you’re being rate limited. If the status is 200 and the body is JSON, look for a byte order mark at the start. serde_json doesn’t skip one, so a file saved as “UTF-8 with BOM” fails at column 1 too. body.trim_start_matches('\u{feff}') removes it.
What each message means
Every serde_json error has the same shape: the reason, then at line L column C. These are syntax errors, which mean the text isn’t valid JSON:
expected value at line 1 column 1 | The text isn’t JSON. Usually it’s an HTML error or login page, or a plain-text message like Not Found. Fix |
EOF while parsing a value at line 1 column 0 | The body was empty, such as a 204 response or a body that had already been read. Fix |
EOF while parsing an object (or a list, or a string) | The JSON stops partway through, so it was cut off. Fix |
key must be a string | A property name without double quotes, or in single quotes. Fix |
trailing comma | A comma before } or ]. Fix |
expected `,` or `}` (or `]`) | A missing comma between items, or a double quote inside a string that isn’t escaped. Fix |
expected `:` | A property name with no colon after it. |
trailing characters | More text after the JSON ends, such as a second object. Line-by-line JSON needs serde_json::Deserializer::from_str(…).into_iter(). Fix |
control character (\u0000-\u001F) found while parsing a string | A real line break or tab inside a string. Fix |
expected ident | A word that starts like true, false or null but isn’t one, such as nil or nan. |
invalid number | A number JSON doesn’t allow, such as 01, or 1. with no digits after the point. |
The second kind comes from serde. The JSON parsed fine, but a value doesn’t fit your type. For these, the line and column point just after the value that didn’t fit, or after the closing } for a missing field:
missing field `first_name` | The JSON doesn’t have that field. Often it’s there as firstName. Fix |
invalid type: string "34", expected u32 | A number sent in quotes, or any other value of the wrong type. Fix |
invalid type: null, expected a string | The value can be null. Make the field an Option. Fix |
invalid type: map, expected a sequence | You asked for a Vec and the JSON has an object, usually with the list inside it. Fix |
unknown variant `shipped`, expected `Pending` or `Shipped` | An enum value spelled differently in the JSON. Fix |
unknown field `email`, expected `id` or `name` | Only with #[serde(deny_unknown_fields)]. Without it, serde skips fields it doesn’t know. |
data did not match any variant of untagged enum Payload | None of the variants fit. Deserialize into each variant on its own to see its real error. |
To tell the two kinds apart in code, err.classify() returns Category::Syntax, Category::Eof or Category::Data. To find the spot in the JSON, paste both the message and the JSON into the error lookup. It converts serde_json’s byte column for you, which matters when the text has accented letters or emoji.
When the JSON doesn’t fit your struct
The most common data error is missing field `first_name` when the JSON clearly has the data. Rust fields are written in snake_case and most APIs use camelCase, so serde looks for first_name and the JSON has firstName. One attribute on the struct fixes every field. The same idea works for enums, which serde otherwise matches by their exact Rust name:
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct User {
id: u64,
first_name: String, // reads "firstName"
#[serde(default)]
tags: Vec<String>, // empty if "tags" is missing
middle_name: Option<String>, // None if missing or null
status: Status,
}
#[derive(Deserialize)]
#[serde(rename_all = "lowercase")]
enum Status {
Pending, // reads "pending"
Shipped,
}An Option field accepts both a missing field and null, so use it for anything the API sometimes leaves out. #[serde(default)] fills in the type’s default value instead. For a single field with an odd name, use #[serde(rename = "user-id")].
For invalid type: string "34", expected u32, the API sends numbers in quotes. If you can’t change it, read the field as a String and convert it, or use DisplayFromStr from the serde_with crate to do the same on the field itself.
A list wrapped in an object
If the whole response fails with invalid type: map, expected a sequence, you asked for a Vec and got an object. Many APIs put the list inside one, next to paging details. Add a small struct for the outer object:
// {"data": [{"id": 1, ...}], "page": 1}
#[derive(Deserialize)]
struct Page {
data: Vec<User>,
}
let users = serde_json::from_str::<Page>(&body)?.data;If you don’t know the shape yet, parse into serde_json::Value first and print it, or paste the response into the JSON viewer.
The same error elsewhere
| Rust, printed with unwrap() | Error("expected value", line: 1, column: 1) |
|---|---|
| Rust, reqwest | error decoding response body |
| Python | JSONDecodeError: Expecting value: line 1 column 1 (char 0) |
| Go | invalid character '<' looking for beginning of value |
| JavaScript | Unexpected token '<', "<!DOCTYPE "... is not valid JSON |