a-b-street/abstreet · error

Can't write_json( )

Error message

Can't write_json({}): {}

What it means

write_json serializes obj to JSON and writes it to path, panicking with this message if any I/O step fails. Note that a path not ending in .json causes a different panic inside maybe_write_json ('write_json needs ... to end with .json'), and parent-directory creation failure panics separately. This specific panic fires on File::create or write_all errors from fs_err.

Solutions

  1. Verify the parent directory is writable and the filesystem isn't read-only or full (df, mount flags).
  2. Check the path ends with .json and its parent is a valid directory before calling.
  3. If failure should be tolerable, call maybe_write_json-equivalent by using abstio::write_file or handle Result-returning APIs instead.
  4. Fix permissions (chmod/chown) on the output directory or run with an account that can write there.

Example fix

// before
abstio::write_json("/mnt/ro/output/stats.json".to_string(), &stats);

// after
let path = "/mnt/ro/output/stats.json";
if std::path::Path::new(path).parent().map_or(false, |d| d.is_dir()) {
    abstio::write_json(path.to_string(), &stats);
} else {
    eprintln!("skipping write: {} is not writable", path);
}
Defensive patterns

Strategy: validation

Validate before calling

let p = std::path::Path::new(&path);
if !path.ends_with(".json") {
    eprintln!("write_json requires .json extension: {}", path);
}
if let Some(parent) = p.parent() {
    if !parent.is_dir() {
        eprintln!("parent dir {:?} missing", parent);
    }
}

Type guard

fn is_writable_json_path(path: &str) -> bool {
    let p = std::path::Path::new(path);
    path.ends_with(".json")
        && p.parent().map_or(false, |d| d.is_dir())
        && std::fs::OpenOptions::new().write(true).create(true).open(p).is_ok()
}

Try / catch

// write_json panics; probe writability first or use a Result-returning writer:
match std::fs::File::create(&path) {
    Ok(_) => abstio::write_json(path.clone(), &obj),
    Err(e) => eprintln!("can't write {}: {}", path, e),
}

Prevention

When it happens

Trigger: Calling write_json(path, obj) where the target file cannot be created (read-only filesystem, permission denied, invalid path) or cannot be fully written (disk full, I/O error, broken pipe when writing to special files).

Common situations: Writing to a read-only output directory or container filesystem; disk quota/full disk on long runs; path components that aren't valid; output paths mounted from a volume without write permission.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of a-b-street/abstreet@0964f29315 (2026-09-13). Data as JSON: /api/errors/eb1060c5b0b302fe. Report an issue: GitHub.

Appendix: source

Thrown at abstio/src/io_native.rs:74

    bincode::deserialize_from(timer).map_err(|err| err.into())
}

// TODO Idea: Have a wrapper type DotJSON(...) and DotBin(...) to distinguish raw path strings
fn maybe_write_json<T: Serialize>(path: &str, obj: &T) -> Result<()> {
    if !path.ends_with(".json") {
        panic!("write_json needs {} to end with .json", path);
    }
    fs_err::create_dir_all(std::path::Path::new(path).parent().unwrap())
        .expect("Creating parent dir failed");

    let mut file = File::create(path)?;
    file.write_all(to_json(obj).as_bytes())?;
    Ok(())
}

pub fn write_json<T: Serialize>(path: String, obj: &T) {
    if let Err(err) = maybe_write_json(&path, obj) {
        panic!("Can't write_json({}): {}", path, err);
    }
    info!("Wrote {}", path);
}

fn maybe_write_binary<T: Serialize>(path: &str, obj: &T) -> Result<()> {
    if !path.ends_with(".bin") {
        panic!("write_binary needs {} to end with .bin", path);
    }

    fs_err::create_dir_all(std::path::Path::new(path).parent().unwrap())
        .expect("Creating parent dir failed");

    let file = BufWriter::new(File::create(path)?);
    bincode::serialize_into(file, obj).map_err(|err| err.into())
}

pub fn write_binary<T: Serialize>(path: String, obj: &T) {
    if let Err(err) = maybe_write_binary(&path, obj) {

View on GitHub (pinned to 0964f29315)