Schniz/fnm · error
Invalid OS string
Error message
Invalid OS string
What it means
To resolve the current version, fnm canonicalizes the multishell symlink, takes its parent directory's file name (the installed version directory under `node-versions/`), and converts it to `&str` with `.expect("Invalid OS string")`. If that directory name contains bytes that are not valid UTF-8, `to_str()` returns None and `fnm current` (and any status display) panics.
Source
Thrown at src/current_version.rs:22
use crate::system_version;
use crate::version::Version;
pub fn current_version(config: &FnmConfig) -> Result<Option<Version>, Error> {
let multishell_path = config.multishell_path().ok_or(Error::EnvNotApplied)?;
if multishell_path.read_link().ok() == Some(system_version::path()) {
return Ok(Some(Version::Bypassed));
}
if let Ok(resolved_path) = std::fs::canonicalize(multishell_path) {
let installation_path = resolved_path
.parent()
.expect("multishell path can't be in the root");
let file_name = installation_path
.file_name()
.expect("Can't get filename")
.to_str()
.expect("Invalid OS string");
let version = Version::parse(file_name).map_err(|source| Error::VersionError {
source,
version: file_name.to_string(),
})?;
Ok(Some(version))
} else {
Ok(None)
}
}
#[derive(Debug, Error)]
pub enum Error {
#[error("`fnm env` was not applied in this context.\nCan't find fnm's environment variables")]
EnvNotApplied,
#[error("Can't read the version as a valid semver")]
VersionError {
source: node_semver::SemverError,
version: String,View on GitHub (pinned to 86adc9676c)
Solutions
- Find offending names: `ls FNM_DIR/node-versions | iconv -f UTF-8 -t UTF-8` — the iconv error points at the bad entry.
- Recreate the alias properly: `fnm alias <installed-version> default` after removing the mangled directory.
- Reinstall the affected version: `fnm uninstall <ver> && fnm install <ver>`.
- Keep FNM_DIR on a native filesystem and avoid creating entries there with non-UTF-8 tooling.
Example fix
# before mkdir $'FNM_DIR/node-versions/v20\xff.0' # stray-byte name fnm current # panic: Invalid OS string # after rm -r $'FNM_DIR/node-versions/v20\xff.0' fnm install 20 && fnm alias 20 default fnm current # v20.x.y
Defensive patterns
Strategy: validation
Validate before calling
fn versions_dir_is_utf8(base: &Path) -> bool {
let dir = base.join("node-versions");
std::fs::read_dir(&dir)
.map(|rd| rd.filter_map(|e| e.ok()).all(|e| e.file_name().to_str().is_some()))
.unwrap_or(false)
} Type guard
fn is_utf8_os_string(s: &std::ffi::OsStr) -> bool {
s.to_str().is_some()
} Try / catch
match current_version(&multishell_path) {
Ok(v) => println!("{}", v),
Err(Error::VersionError { version, .. }) =>
eprintln!("version dir '{version}' is malformed; recreate it via fnm install/alias"),
Err(e) => return Err(e.into()),
} Prevention
- Only create entries under node-versions via fnm commands (install/alias).
- After restoring from backup, run an iconv sweep over node-versions names.
- Avoid scripting `ln -s` into node-versions with unvalidated input.
When it happens
Trigger: Running `fnm current` (or commands showing current status) when the version/alias directory the multishell points to under FNM_DIR/node-versions has a non-UTF-8 name — e.g. a manually created alias directory with stray bytes, or names mangled by a restore.
Common situations: Hand-crafted version dirs or aliases (`ln -s` to oddly named folders); FNM_DIR restored from a backup with encoding damage; filesystems mounted with names not stored as UTF-8; scripts creating alias dirs from untrusted input.
Related errors
- Can't convert path to string
- Can't convert path to string
- Can't read PATH
- Can't convert path to string
- Path is not valid UTF-8
AI-assisted analysis of Schniz/fnm@86adc9676c (2026-08-16).
Data as JSON: /api/errors/11bfaeece079805d.
Report an issue: GitHub.