a-b-street/abstreet · error
read_json needs to end with .json or .geojson
Error message
read_json needs {} to end with .json or .geojson What it means
abstio::maybe_read_json deserializes JSON files and enforces that the path ends with .json or .geojson before reading. This guards against accidentally passing binary (bincode) files or paths without an extension; otherwise it bails with "read_json needs ... to end with .json or .geojson".
Solutions
- Fix the path so it ends with .json or .geojson.
- If the file is actually bincode, use abstio::maybe_read_binary instead.
- If the path points to a directory, point at the concrete .json file inside it.
Example fix
// before
abstio::maybe_read_json("../data/system/cities.bin", timer)
// after
abstio::maybe_read_binary::<MyType>("../data/system/cities.bin", timer) Defensive patterns
Strategy: validation
Validate before calling
fn ensure_json_path(path: &str) -> Result<(), String> {
if path.ends_with(".json") || path.ends_with(".geojson") { Ok(()) } else { Err(format!("{} is not a .json/.geojson path", path)) }
} Try / catch
if let Err(e) = ensure_json_path(&path) { bail!("{}", e); }
let value: T = abstio::maybe_read_json(path, timer)?; Prevention
- Use maybe_read_binary for .bin/bincode files, read_json only for .json/.geojson.
- Build paths with helpers like abstio::path rather than raw string concatenation.
- Keep a single constant for data file extensions.
When it happens
Trigger: Calling abstio::read_json / read_object with a path ending in .bin, .txt, no extension, or a different extension; passing a path to a bincode-serialized system file.
Common situations: Confusing binary data files (produced by abstio::write_binary) with JSON ones; path constructed by string concatenation that dropped the extension; passing a directory path instead of a file.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13).
Data as JSON: /api/errors/40995ca760a5f973.
Report an issue: GitHub.
Appendix: source
Thrown at abstio/src/io.rs:12
use std::collections::BTreeMap;
use anyhow::Result;
use serde::de::DeserializeOwned;
use abstutil::{basename, parent_path, Timer};
use crate::{list_dir, maybe_read_binary, slurp_file};
pub fn maybe_read_json<T: DeserializeOwned>(path: String, timer: &mut Timer) -> Result<T> {
if !path.ends_with(".json") && !path.ends_with(".geojson") {
bail!("read_json needs {} to end with .json or .geojson", path);
}
timer.start(format!("parse {}", path));
// TODO timer.read_file isn't working here. And we need to call stop() if there's no file.
let result: Result<T> =
slurp_file(&path).and_then(|raw| serde_json::from_slice(&raw).map_err(|err| err.into()));
timer.stop(format!("parse {}", path));
result
}
pub fn read_json<T: DeserializeOwned>(path: String, timer: &mut Timer) -> T {
match maybe_read_json(path.clone(), timer) {
Ok(obj) => obj,
Err(err) => panic!("Couldn't read_json({}): {}", path, err),
}
}
pub fn read_binary<T: DeserializeOwned>(path: String, timer: &mut Timer) -> T {View on GitHub (pinned to 0964f29315)