{"record":{"id":"8d4c30f4249ac9e3","repo":"flxzt/rnote","slug":"file-of-source-path-adjusted-source-path-doe","errorCode":null,"errorMessage":"file of source path '{adjusted_source_path:?}' does not have a file stem.","messagePattern":"file of source path '(.+?)' does not have a file stem\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/rnote-ui/src/workspacebrowser/filerow/actions/duplicate.rs","lineNumber":103,"sourceCode":"        &[source.as_ref()],\n        destination,\n        &fs_extra::dir::CopyOptions {\n            copy_inside: true,\n            ..Default::default()\n        },\n    )?;\n    Ok(())\n}\n\n/// returns a suitable not-already-existing destination path from the given source path\n/// by adding or replacing `<delim><num>` to the source-path, where `<num>` is incremented as often as needed.\nfn generate_destination_path(source: impl AsRef<Path>) -> anyhow::Result<PathBuf> {\n    let mut duplicate_index = 1;\n    let mut destination_path = source.as_ref().to_owned();\n    let adjusted_source_path = remove_dup_suffix(source);\n\n    let Some(source_stem) = adjusted_source_path.file_stem() else {\n        return Err(anyhow::anyhow!(\n            \"file of source path '{adjusted_source_path:?}' does not have a file stem.\"\n        ));\n    };\n\n    let source_extension = adjusted_source_path.extension();\n\n    // Loop to find the next available duplicate filename\n    loop {\n        destination_path.set_file_name(generate_duplicate_filename(\n            source_stem,\n            source_extension,\n            duplicate_index,\n        ));\n\n        if !destination_path.exists() {\n            return Ok(destination_path);\n        }\n","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/flxzt/rnote/blob/bbc5354502ba2fc83eec2670b535348825e679a6/crates/rnote-ui/src/workspacebrowser/filerow/actions/duplicate.rs#L85-L121","documentation":"This error is thrown by generate_destination_path when the adjusted source path has no file stem. Path::file_stem() returns None for paths that end in '..' or '/', or for paths whose final component is empty or dot-only (e.g. '.', '..'), or for an empty path. Since the duplicate operation needs a stem to build 'name (copy 2).ext', it cannot proceed and errors out.","triggerScenarios":"Calling duplicate_file or duplicate_dir with a source path whose final component has no stem after remove_dup_suffix — typically an empty path, a path ending in '/', '.', or '..', or a path whose name consisted solely of duplicate suffix text that remove_dup_suffix stripped away.","commonSituations":"User triggers Duplicate in the workspace browser on a malformed/blank row entry (workspace entry with empty relative path), a path built from unvalidated user input or a stale index row, or an edge-case file literally named '.' or '..' after suffix removal.","solutions":["Validate the source path before calling duplicate: ensure it is non-empty, has a file_name()/file_stem(), and points to an existing file or directory in the workspace.","Check that remove_dup_suffix cannot reduce the filename to nothing; if the whole stem matched the duplicate suffix pattern, fall back to the original name instead of the stripped one.","Guard the workspace-browser UI so Duplicate is only enabled for rows backed by a valid file path (disable action when the row's path is empty).","When the error occurs, skip the row and log it rather than aborting the whole batch of duplicates."],"exampleFix":"// before\nlet dest = generate_destination_path(&entry.path)?;\n// after\nif entry.path.as_os_str().is_empty() || entry.path.file_stem().is_none() {\n    anyhow::bail!(\"skipping duplicate: '{}' has no valid file name\", entry.path.display());\n}\nlet dest = generate_destination_path(&entry.path)?;","handlingStrategy":"validation","validationCode":"fn validate_duplicate_source(source: &std::path::Path) -> anyhow::Result<()> {\n    if source.as_os_str().is_empty() {\n        anyhow::bail!(\"source path is empty\");\n    }\n    if source.file_stem().is_none() {\n        anyhow::bail!(\"source path '{}' has no file stem\", source.display());\n    }\n    Ok(())\n}","typeGuard":"fn has_file_stem(source: &std::path::Path) -> bool {\n    source.file_stem().map(|s| !s.is_empty()).unwrap_or(false)\n}","tryCatchPattern":"match generate_destination_path(&source) {\n    Ok(dest) => duplicate_at(dest),\n    Err(e) if e.to_string().contains(\"does not have a file stem\") => {\n        log::warn!(\"skipping invalid duplicate source: {e}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate workspace entries at load time so rows never carry empty or malformed relative paths","Only enable the Duplicate action when the row has a non-empty, stem-bearing file name","After any name transformation (e.g. remove_dup_suffix), re-verify the result still yields a non-empty stem before building the destination","Use Path::file_name()/file_stem() checks as a precondition instead of relying on the error to catch bad input"],"tags":["path","filesystem","file-stem","duplicate","validation"],"backgroundTag":"invalid-argument-value","analyzedSha":"bbc5354502ba2fc83eec2670b535348825e679a6","analyzedAt":"2026-09-08T13:20:33.747Z","contentChangedAt":"2026-09-08T13:20:33.747Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}