{"record":{"id":"11bfaeece079805d","repo":"Schniz/fnm","slug":"invalid-os-string","errorCode":null,"errorMessage":"Invalid OS string","messagePattern":"Invalid OS string","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/current_version.rs","lineNumber":22,"sourceCode":"use crate::system_version;\nuse crate::version::Version;\n\npub fn current_version(config: &FnmConfig) -> Result<Option<Version>, Error> {\n    let multishell_path = config.multishell_path().ok_or(Error::EnvNotApplied)?;\n\n    if multishell_path.read_link().ok() == Some(system_version::path()) {\n        return Ok(Some(Version::Bypassed));\n    }\n\n    if let Ok(resolved_path) = std::fs::canonicalize(multishell_path) {\n        let installation_path = resolved_path\n            .parent()\n            .expect(\"multishell path can't be in the root\");\n        let file_name = installation_path\n            .file_name()\n            .expect(\"Can't get filename\")\n            .to_str()\n            .expect(\"Invalid OS string\");\n        let version = Version::parse(file_name).map_err(|source| Error::VersionError {\n            source,\n            version: file_name.to_string(),\n        })?;\n        Ok(Some(version))\n    } else {\n        Ok(None)\n    }\n}\n\n#[derive(Debug, Error)]\npub enum Error {\n    #[error(\"`fnm env` was not applied in this context.\\nCan't find fnm's environment variables\")]\n    EnvNotApplied,\n    #[error(\"Can't read the version as a valid semver\")]\n    VersionError {\n        source: node_semver::SemverError,\n        version: String,","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/Schniz/fnm/blob/86adc9676ceb2a509b21e75e74048b93c89f097d/src/current_version.rs#L4-L40","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nmkdir $'FNM_DIR/node-versions/v20\\xff.0'   # stray-byte name\nfnm current   # panic: Invalid OS string\n\n# after\nrm -r $'FNM_DIR/node-versions/v20\\xff.0'\nfnm install 20 && fnm alias 20 default\nfnm current   # v20.x.y","handlingStrategy":"validation","validationCode":"fn versions_dir_is_utf8(base: &Path) -> bool {\n    let dir = base.join(\"node-versions\");\n    std::fs::read_dir(&dir)\n        .map(|rd| rd.filter_map(|e| e.ok()).all(|e| e.file_name().to_str().is_some()))\n        .unwrap_or(false)\n}","typeGuard":"fn is_utf8_os_string(s: &std::ffi::OsStr) -> bool {\n    s.to_str().is_some()\n}","tryCatchPattern":"match current_version(&multishell_path) {\n    Ok(v) => println!(\"{}\", v),\n    Err(Error::VersionError { version, .. }) =>\n        eprintln!(\"version dir '{version}' is malformed; recreate it via fnm install/alias\"),\n    Err(e) => return Err(e.into()),\n}","preventionTips":["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."],"tags":["rust","utf-8","path","current-version","panic","symlink"],"backgroundTag":"non-utf8-path","analyzedSha":"86adc9676ceb2a509b21e75e74048b93c89f097d","analyzedAt":"2026-08-16T21:43:05.725Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}