a-b-street/abstreet · warning · anyhow::Error

no file chosen to save

Error message

no file chosen to save

What it means

In map_gui's FileSaver::new_state, the native (non-wasm) branch shows a save-file dialog; if the user cancels or selects nothing, the builder returns no path and the code errs with anyhow!("no file chosen to save"). This distinguishes user cancellation from a genuine write failure.

Solutions

  1. Treat the Err as expected user cancellation and skip any error UI/teardown
  2. Pre-supply an output path via CLI flags to skip the dialog
  3. Catch the error and log at info level instead of surfacing it

Example fix

// before
FileSaver::new(ctx, "Save edits", contents, "json").consume();
// after
if let Err(err) = FileSaver::new(ctx, "Save edits", contents, "json").consume() {
    log::info!("save cancelled: {}", err);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// confirm a path before attempting the native save flow
if output_path.is_none() && !interactive_dialogs_supported() {
    return; // skip GUI save
}

Try / catch

match FileSaver::new(ctx, "Save edits", contents, "json").consume() {
    Ok(_) => {},
    Err(e) if e.to_string().contains("no file chosen") => { /* user cancelled; ignore */ }
    Err(e) => ctx.toast_error(format!("Save failed: {}", e)),
}

Prevention

When it happens

Trigger: Invoking a save operation (e.g. saving map edits, metrics, or a PNG) on native and dismissing/cancelling the native file-save dialog.

Common situations: Users pressing Escape or Cancel in the save dialog; automated tests that can't interact with the dialog; headless runs of the GUI.

Related errors


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

Appendix: source

Thrown at map_gui/src/tools/ui.rs:94

            ctx,
            Box::pin(async move {
                let mut builder = rfd::AsyncFileDialog::new().set_file_name(&filename);
                if let Some(dir) = start_dir {
                    builder = builder.set_directory(&dir);
                }

                #[cfg(not(target_arch = "wasm32"))]
                let result = if let Some(handle) = builder.save_file().await {
                    let path = handle.path().display().to_string();
                    // Both cases do AsRef<[u8]>
                    match write {
                        FileSaverContents::String(string) => fs_err::write(&path, string),
                        FileSaverContents::Bytes(bytes) => fs_err::write(&path, bytes),
                    }
                    .map(|_| path)
                    .map_err(|err| err.into())
                } else {
                    Err(anyhow!("no file chosen to save"))
                };

                #[cfg(target_arch = "wasm32")]
                let result = {
                    // Hide an unused warning
                    let _ = builder;

                    // TODO No file save dialog on WASM until
                    // https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker
                    match write {
                        FileSaverContents::String(string) => {
                            abstio::write_file(filename.clone(), string)
                        }
                        FileSaverContents::Bytes(_bytes) => {
                            // We need to use write_file (which downloads a file), but encode
                            // binary data the right way
                            Err(anyhow!("writing binary files on web unsupported"))
                        }

View on GitHub (pinned to 0964f29315)