GitoxideLabs/gitoxide · error

no illformed UTF-8

Error message

no illformed UTF-8

What it means

The second expect in `worktree::Proxy::id()`: `os_str_into_bstr(...).expect("no illformed UTF-8")` panics when the worktree directory name cannot be represented as UTF-8-backed bytes. On Unix this means the directory name contains invalid UTF-8; on Windows, non-representable wide characters per the conversion rules.

Solutions

  1. Rename the worktree directory to a valid UTF-8 name (prefer `git worktree add` with ASCII names).
  2. Validate the name: check `file_name().to_str().is_some()` before invoking worktree APIs.
  3. Normalize tooling/locales that create worktrees so names are always UTF-8.

Example fix

// before
let id = proxy.id(); // panics on non-UTF-8 dir name
// after
let name = proxy.git_dir().file_name().and_then(|n| n.to_str());
if name.is_none() { eprintln!("worktree name is not valid UTF-8"); return; }
let id = proxy.id();
Defensive patterns

Strategy: validation

Validate before calling

let ok = proxy.git_dir().file_name().map_or(false, |n| n.to_str().is_some());
if !ok { return Err(anyhow::anyhow!("worktree name must be valid UTF-8")); }

Type guard

fn utf8_named(p: &std::path::Path) -> bool { p.file_name().map_or(false, |n| n.to_str().is_some()) }

Prevention

When it happens

Trigger: Calling `worktree::Proxy::id()` when the worktree's directory name under `worktrees/` contains non-UTF-8 bytes or unencodable characters.

Common situations: Linux filesystems allowing arbitrary bytes in names; worktrees created by non-UTF-8-aware scripts or third-party tools; locale mismatch when creating worktrees.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/37ca782f83c0f4d4. Report an issue: GitHub.

Appendix: source

Thrown at gix/src/worktree/proxy.rs:70

            )
        })?
    }

    /// Read the location of the checkout, the base of the work tree.
    /// Note that the location might not exist.
    pub fn base(&self) -> std::io::Result<PathBuf> {
        Ok(gix_discover::path::without_dot_git_dir(self.dot_git()?))
    }

    /// The git directory for the work tree, typically contained within the parent git dir.
    pub fn git_dir(&self) -> &Path {
        &self.git_dir
    }

    /// The name of the worktree, which is derived from its folder within the `worktrees` directory within the parent `.git` folder.
    pub fn id(&self) -> &BStr {
        gix_path::os_str_into_bstr(self.git_dir.file_name().expect("worktrees/ parent dir"))
            .expect("no illformed UTF-8")
    }

    /// Return true if the worktree cannot be pruned, moved or deleted, which is useful if it is located on an external storage device.
    pub fn is_locked(&self) -> bool {
        self.git_dir.join("locked").symlink_metadata().is_ok()
    }

    /// Return true if this worktree can be pruned without an expiry grace period.
    ///
    /// Locked worktrees are never prunable. Otherwise, an unreadable `gitdir` file or missing target
    /// makes the worktree prunable.
    pub fn is_prunable(&self) -> bool {
        !self.is_locked()
            && self
                .dot_git()
                .map_or(true, |dot_git| dot_git.symlink_metadata().is_err())
    }

View on GitHub (pinned to e73179060b)