{"record":{"id":"4e69d7456c1df83e","repo":"a-b-street/abstreet","slug":"no-file-chosen-to-save","errorCode":null,"errorMessage":"no file chosen to save","messagePattern":"no file chosen to save","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"warning","filePath":"map_gui/src/tools/ui.rs","lineNumber":94,"sourceCode":"            ctx,\n            Box::pin(async move {\n                let mut builder = rfd::AsyncFileDialog::new().set_file_name(&filename);\n                if let Some(dir) = start_dir {\n                    builder = builder.set_directory(&dir);\n                }\n\n                #[cfg(not(target_arch = \"wasm32\"))]\n                let result = if let Some(handle) = builder.save_file().await {\n                    let path = handle.path().display().to_string();\n                    // Both cases do AsRef<[u8]>\n                    match write {\n                        FileSaverContents::String(string) => fs_err::write(&path, string),\n                        FileSaverContents::Bytes(bytes) => fs_err::write(&path, bytes),\n                    }\n                    .map(|_| path)\n                    .map_err(|err| err.into())\n                } else {\n                    Err(anyhow!(\"no file chosen to save\"))\n                };\n\n                #[cfg(target_arch = \"wasm32\")]\n                let result = {\n                    // Hide an unused warning\n                    let _ = builder;\n\n                    // TODO No file save dialog on WASM until\n                    // https://developer.mozilla.org/en-US/docs/Web/API/Window/showSaveFilePicker\n                    match write {\n                        FileSaverContents::String(string) => {\n                            abstio::write_file(filename.clone(), string)\n                        }\n                        FileSaverContents::Bytes(_bytes) => {\n                            // We need to use write_file (which downloads a file), but encode\n                            // binary data the right way\n                            Err(anyhow!(\"writing binary files on web unsupported\"))\n                        }","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/a-b-street/abstreet/blob/0964f29315820c91b171b585eb51e300164e9197/map_gui/src/tools/ui.rs#L76-L112","documentation":"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.","triggerScenarios":"Invoking a save operation (e.g. saving map edits, metrics, or a PNG) on native and dismissing/cancelling the native file-save dialog.","commonSituations":"Users pressing Escape or Cancel in the save dialog; automated tests that can't interact with the dialog; headless runs of the GUI.","solutions":["Treat the Err as expected user cancellation and skip any error UI/teardown","Pre-supply an output path via CLI flags to skip the dialog","Catch the error and log at info level instead of surfacing it"],"exampleFix":"// before\nFileSaver::new(ctx, \"Save edits\", contents, \"json\").consume();\n// after\nif let Err(err) = FileSaver::new(ctx, \"Save edits\", contents, \"json\").consume() {\n    log::info!(\"save cancelled: {}\", err);\n}","handlingStrategy":"try-catch","validationCode":"// confirm a path before attempting the native save flow\nif output_path.is_none() && !interactive_dialogs_supported() {\n    return; // skip GUI save\n}","typeGuard":null,"tryCatchPattern":"match FileSaver::new(ctx, \"Save edits\", contents, \"json\").consume() {\n    Ok(_) => {},\n    Err(e) if e.to_string().contains(\"no file chosen\") => { /* user cancelled; ignore */ }\n    Err(e) => ctx.toast_error(format!(\"Save failed: {}\", e)),\n}","preventionTips":["Treat dialog cancellation as a normal outcome, not an error","Offer CLI/config-supplied output paths for automation","Don't wrap the result in error toasts without filtering cancellations"],"tags":["file-dialog","user-cancel","native","ui"],"backgroundTag":"user-cancelled-operation","analyzedSha":"0964f29315820c91b171b585eb51e300164e9197","analyzedAt":"2026-09-13T18:02:03.421Z","contentChangedAt":"2026-09-13T18:02:03.421Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}