flxzt/rnote · error · anyhow::Error

Could not get a path for file

Error message

Could not get a path for file: `{file:?}`.

What it means

save_document_to_file writes the document to a user-chosen Gio::File. The writer needs a real filesystem path (and basename) to serialize to; when the chosen File has no path (non-native location such as a portal handle or unusual URI), path() is None, save-in-progress is reset, and this error is thrown.

Solutions

  1. Choose a native local directory in the save dialog (local filesystem path), then move/copy the saved file to remote targets afterwards.
  2. In the caller, check file.path().is_some() before invoking save_document_to_file and fall back to load_bytes + temp-file write + copy for pathless files.
  3. Use file.load_bytes()/create-on-temp then gio copy into the destination File instead of requiring a direct path.

Example fix

// before
let filepath = file.path().ok_or_else(|| {
    self.set_save_in_progress(false);
    anyhow::anyhow!("Could not get a path for file: `{file:?}`.")
})?;
// after
let Some(filepath) = file.path() else {
    self.set_save_in_progress(false);
    anyhow::bail!(
        "Cannot save to non-native location {file:?}; pick a local folder."
    );
};
Defensive patterns

Strategy: validation

Validate before calling

// rust
if file.path().is_none() {
    eprintln!("target {:?} has no native path; choose a local folder or copy the result", file.uri());
}

Type guard

fn is_native_target(f: &gio::File) -> bool { f.path().is_some() }

Try / catch

match canvas.save_document_to_file(&file, ...).await {
    Ok(()) => {},
    Err(e) if e.to_string().contains("Could not get a path") => {
        // save to temp file, then gio copy into `file`
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: dialog_save_doc_as -> save_document_to_file with a File chosen from a non-native source (gvfs/network location, document portal fd, URI without file:// scheme) where file.path() returns None.

Common situations: Saving via flatpak document portal into odd locations; picking an MTP/phone or network share target in the save dialog whose Gio::File has no native path; passing a constructed URI instead of a local path to the save API.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


AI-assisted analysis of flxzt/rnote@bbc5354502 (2026-09-08). Data as JSON: /api/errors/5ac28ee2199f195a. Report an issue: GitHub.

Appendix: source

Thrown at crates/rnote-ui/src/canvas/imexport.rs:229

    /// Saves the document to the given file.
    ///
    /// Returns:
    /// - `Ok(true)` if saving was successful
    /// - `Ok(false)` if a save was already in progress (and thus this function didn't do anything)
    /// - `Err(e)` when saving failed in any way
    #[tracing::instrument(skip_all, fields(path = format!("{:?}", file.path())))]
    pub(crate) async fn save_document_to_file(&self, file: &gio::File) -> anyhow::Result<bool> {
        // skip saving when it is already in progress
        if self.save_in_progress() {
            debug!("Returning early, saving file is already in progress");
            return Ok(false);
        }
        self.set_save_in_progress(true);
        debug!("Saving file is now in progress");

        let filepath = file.path().ok_or_else(|| {
            self.set_save_in_progress(false);
            anyhow::anyhow!("Could not get a path for file: `{file:?}`.")
        })?;
        let basename = file.basename().ok_or_else(|| {
            self.set_save_in_progress(false);
            anyhow::anyhow!("Could not retrieve basename for file: `{file:?}`.")
        })?;
        let rnote_bytes_receiver = self
            .engine_ref()
            .save_as_rnote_bytes(basename.to_string_lossy().to_string());

        let mut skip_set_output_file = false;
        if let Some(output_filepath) = self.output_file().and_then(|f| f.path())
            && crate::utils::paths_abs_eq(output_filepath, &filepath).unwrap_or(false)
        {
            skip_set_output_file = true;
        }

        self.dismiss_output_file_modified_toast();

View on GitHub (pinned to bbc5354502)