sinelaw/fresh · error

buffer.cannot_open_directory

Error message

buffer.cannot_open_directory

What it means

open_file_no_focus_inner uses filesystem.is_dir (after canonicalizing, so symlinks are resolved) and bails with a localized message when the target path is a directory, since directories cannot be opened as editable buffers.

Solutions

  1. Check path.is_dir() before calling the open API and handle directories separately (e.g. open a file listing).
  2. Pass a concrete file path inside the directory instead of the directory itself.
  3. Resolve symlinks and verify the target is a regular file before opening.

Example fix

// before
editor.open_file("/home/user/project")?; // a directory
// after
let p = Path::new("/home/user/project");
if p.is_dir() { editor.open_directory_listing(p)?; } else { editor.open_file(p)?; }
Defensive patterns

Strategy: validation

Validate before calling

let canon = std::fs::canonicalize(path)?; if canon.is_dir() { open_listing(&canon)?; return Ok(()); }

Type guard

fn is_openable_file(p: &Path) -> bool { std::fs::canonicalize(p).map(|c| c.is_file()).unwrap_or(false) }

Try / catch

if let Err(e) = editor.open_file(path) { if e.to_string().contains("cannot_open_directory") { editor.open_directory_listing(path)?; } }

Prevention

When it happens

Trigger: Calling open_file_no_focus / open_file_no_focus_with_kind / open_file_for_preview with a path that canonicalizes to a directory (is_dir returns true).

Common situations: Passing a directory path to a 'open file' command; opening a symlink that points at a directory; LSP or plugin handing the editor a folder URI.

Related errors


AI-assisted analysis of sinelaw/fresh@67894ca546 (2026-09-13). Data as JSON: /api/errors/4860972002122e8a. Report an issue: GitHub.

Appendix: source

Thrown at crates/fresh-editor/src/app/file_open_orchestrators.rs:1142

                        .canonicalize(parent)
                        .unwrap_or_else(|_| parent.to_path_buf())
                };
                if let Some(filename) = resolved_path.file_name() {
                    canonical_parent.join(filename)
                } else {
                    resolved_path
                }
            } else {
                resolved_path
            }
        };
        let path = canonical_path.as_path();

        // Check if the path is a directory (after following symlinks via canonicalize)
        // Directories cannot be opened as files in the editor
        // Use filesystem trait method to support remote files
        if self.authority().filesystem.is_dir(path).unwrap_or(false) {
            anyhow::bail!(t!("buffer.cannot_open_directory"));
        }

        // Check if file is already open - return existing buffer without switching
        let already_open = self
            .buffers
            .iter()
            .find(|(_, state)| state.buffer.file_path() == Some(path))
            .map(|(id, _)| *id);

        if let Some(id) = already_open {
            return Ok(id);
        }

        // If the current buffer is empty and unmodified, replace it instead of creating a new one
        // Note: Don't replace composite buffers (they appear empty but are special views).
        // Suppressed when `allow_replace_empty` is false — see
        // `open_file_for_preview` for the rationale.
        let replace_current = allow_replace_empty && {

View on GitHub (pinned to 67894ca546)