🎓 A1Learner
🔍

Rust JSON Error Messages

A JSON error message names the problem and the place where reading failed.

Print the error, skip the unwrap

The error's Display text is a full sentence. It ends with the line and column of the trouble.

let result = serde_json::from_str::<serde_json::Value>("{\"name\": }");
match result {
    Ok(_) => println!("fine"),
    Err(e) => println!("{e}"),
}

A missing field names itself

Person is the struct from the JSON lesson, with name and age. The struct needed age. The text never sent it.

let p = serde_json::from_str::<Person>("{\"name\":\"Ada\"}");
println!("{}", p.unwrap_err());

A wrong type names both sides

The message gives the value it found. It also gives the type it expected.

let p = serde_json::from_str::<Person>("{\"name\":\"Ada\",\"age\":\"old\"}");
println!("{}", p.unwrap_err());

Ask for the place as numbers

line and column are methods on the error. Programs can log them. Programs can also point into the text with them.

if let Err(e) = serde_json::from_str::<serde_json::Value>("[1, 2") {
    println!("line {} column {}", e.line(), e.column());
}

Sort errors by kind

Three test methods split errors into families. A missing field is a data error, not broken JSON.

let e = serde_json::from_str::<Person>("{\"name\":\"Ada\"}").unwrap_err();
println!("syntax={} data={} eof={}", e.is_syntax(), e.is_data(), e.is_eof());

Extra text after the value

from_str wants exactly one value. Anything after it is an error too.

println!("{}", serde_json::from_str::<serde_json::Value>("{} {}").unwrap_err());

Example

// Cargo.toml: serde = { version = "1.0.229", features = ["derive"] }, serde_json = "1.0.154"
use serde::Deserialize;

#[derive(Deserialize, Debug)]
struct Config { port: u32, debug: bool }

fn load(text: &str) -> Result<Config, serde_json::Error> {
    serde_json::from_str(text)
}

fn main() {
    let good = "{\"port\":8080,\"debug\":true}";
    let bad_type = "{\"port\":\"high\",\"debug\":true}";
    let cut = "{\"port\":8080,";
    for text in [good, bad_type, cut] {
        match load(text) {
            Ok(cfg) => println!("loaded: port {}", cfg.port),
            Err(e) => println!("failed: {e}"),
        }
    }
}

Key points

  • A syntax error means the text breaks JSON's own rules.
  • A data error means the JSON is fine but your type is not.
  • An EOF error means the text stopped early.
  • The value was still unfinished.
  • The column points at the spot where reading failed.
  • For cut-off text it points just past the last character.
  • unwrap turns a bad input into a crash.
  • Match on the Result when the text comes from outside.
  • Only the source of the text changes.
  • A JSON error tells what went wrong and where reading stopped.

In this example, one small loader tries three texts. The first text fits Config, so the port prints. The second text sends the port as a word. The error names the value and the type it wanted. The third text stops in the middle. Its error reports the early end. No run crashes. load hands back the Result, and main matches on it.

Note

Errors from a file read look the same.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

A parse fails with the message 'missing field `port`'. What kind of error is it?