{"record":{"id":"f37f7c30a84ec19e","repo":"a-b-street/abstreet","slug":"couldn-t-read-json","errorCode":null,"errorMessage":"Couldn't read_json({}): {}","messagePattern":"Couldn't read_json\\((.+?)\\): (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"abstio/src/io.rs","lineNumber":26,"sourceCode":"use crate::{list_dir, maybe_read_binary, slurp_file};\n\npub fn maybe_read_json<T: DeserializeOwned>(path: String, timer: &mut Timer) -> Result<T> {\n    if !path.ends_with(\".json\") && !path.ends_with(\".geojson\") {\n        bail!(\"read_json needs {} to end with .json or .geojson\", path);\n    }\n\n    timer.start(format!(\"parse {}\", path));\n    // TODO timer.read_file isn't working here. And we need to call stop() if there's no file.\n    let result: Result<T> =\n        slurp_file(&path).and_then(|raw| serde_json::from_slice(&raw).map_err(|err| err.into()));\n    timer.stop(format!(\"parse {}\", path));\n    result\n}\n\npub fn read_json<T: DeserializeOwned>(path: String, timer: &mut Timer) -> T {\n    match maybe_read_json(path.clone(), timer) {\n        Ok(obj) => obj,\n        Err(err) => panic!(\"Couldn't read_json({}): {}\", path, err),\n    }\n}\n\npub fn read_binary<T: DeserializeOwned>(path: String, timer: &mut Timer) -> T {\n    match maybe_read_binary(path.clone(), timer) {\n        Ok(obj) => obj,\n        Err(err) => panic!(\"Couldn't read_binary({}): {}\", path, err),\n    }\n}\n\n/// May be a JSON or binary file\npub fn read_object<T: DeserializeOwned>(path: String, timer: &mut Timer) -> Result<T> {\n    if path.ends_with(\".bin\") {\n        maybe_read_binary(path, timer)\n    } else {\n        maybe_read_json(path, timer)\n    }\n}","sourceCodeStart":8,"sourceCodeEnd":44,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/abstio/src/io.rs#L8-L44","documentation":"abstio::read_json is an infallible wrapper around maybe_read_json: it deserializes a JSON file at the given path, and if the read or parse fails (missing file, bad permissions, malformed JSON, schema mismatch) it panics with the path and underlying error. It exists for cases where the caller considers the file mandatory.","triggerScenarios":"Any call to abstio::read_json::<T>(path, timer) where maybe_read_json fails: the file does not exist at the resolved path, the JSON is syntactically invalid, or the JSON does not match T's serde expectations. Commonly via wrappers like CityName::input_path-based loads of input/system files.","commonSituations":"Typo in the path or scenario/map name, calling read_json on an optional file that hasn't been generated yet, running before data has been imported/downloaded, hand-edited JSON with a syntax error, or deserializing an older/newer file version into a changed struct.","solutions":["Verify the file exists at the exact resolved path (print the path; check with std::path::Path::exists or abstio::file_exists)","Switch to abstio::maybe_read_json and handle the Err gracefully instead of panicking when the file may legitimately be absent","Regenerate or re-download the data (run the importer / data sync) if the file is missing because data wasn't built","Validate the JSON against the expected schema / check serde version compatibility if the error is a parse or deserialization failure"],"exampleFix":"// before\nlet edits: EditMap = abstio::read_json(path, timer); // panics if missing\n// after\nlet edits: EditMap = match abstio::maybe_read_json(path.clone(), timer) {\n    Ok(obj) => obj,\n    Err(err) => {\n        eprintln!(\"optional file {} unavailable: {}\", path, err);\n        EditMap::default()\n    }\n};","handlingStrategy":"try-catch","validationCode":"if !abstio::file_exists(path.clone()) {\n    eprintln!(\"required file missing: {}\", path);\n    return;\n}\nlet data: T = abstio::read_json(path, timer);","typeGuard":null,"tryCatchPattern":"// Prefer the fallible API instead of catching the panic:\nmatch abstio::maybe_read_json::<T>(path.clone(), timer) {\n    Ok(obj) => obj,\n    Err(err) => {\n        eprintln!(\"Couldn't read {}: {}\", path, err);\n        Default::default() // or propagate the error\n    }\n}","preventionTips":["Use maybe_read_json for any file that may legitimately be absent; reserve read_json for mandatory files","Log/print the resolved path so missing-file panics are easy to diagnose","Ensure data has been imported/downloaded before loading input or system files","Regenerate JSON after schema changes so serialized files match current struct versions"],"tags":["rust","panic","json","file-io","deserialization"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}