{"record":{"id":"86ca068711b1efc0","repo":"Hmbown/CodeWhale","slug":"resolving-external-credential-path-err","errorCode":null,"errorMessage":"resolving external credential path: {err}","messagePattern":"resolving external credential path: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/external_credentials.rs","lineNumber":113,"sourceCode":"            | '\\u{200f}'\n            | '\\u{2028}'\n            | '\\u{2029}'\n            | '\\u{202a}'..='\\u{202e}'\n            | '\\u{2066}'..='\\u{2069}'\n    )\n}\n\n/// Resolve a user-selected path without touching the filesystem.\n///\n/// Consent is bound to the exact logical path, so this intentionally avoids\n/// canonicalization (which would stat the candidate before consent exists).\npub fn resolve_external_credential_path(path: impl AsRef<Path>) -> Result<PathBuf> {\n    let path = path.as_ref();\n    let absolute = if path.is_absolute() {\n        path.to_path_buf()\n    } else {\n        std::env::current_dir()\n            .map_err(|err| anyhow::anyhow!(\"resolving external credential path: {err}\"))?\n            .join(path)\n    };\n\n    // Normalize only lexical `.` / `..` components. Canonicalization would\n    // inspect a credential path before consent exists and would also silently\n    // bless a symlink target. The secure reader rejects symlink/reparse-point\n    // components when the granted capability is actually consumed.\n    let mut normalized = PathBuf::new();\n    for component in absolute.components() {\n        match component {\n            Component::Prefix(prefix) => normalized.push(prefix.as_os_str()),\n            Component::RootDir => normalized.push(component.as_os_str()),\n            Component::CurDir => {}\n            Component::ParentDir => {\n                if !normalized.pop() {\n                    bail!(\n                        \"external credential path escapes its absolute root: {}\",\n                        quote_os_path(&absolute)","sourceCodeStart":95,"sourceCodeEnd":131,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/external_credentials.rs#L95-L131","documentation":"This error wraps a failure of std::env::current_dir() while resolving a relative external credential path to an absolute one. The library deliberately avoids canonicalization (to not touch credential paths before consent or bless symlink targets), so it falls back to joining the current working directory. If the process's working directory cannot be determined (e.g. it was deleted), this error is thrown.","triggerScenarios":"Calling resolve_external_credential_path (directly or via validate_read_scope / disclosed_paths_are_absolute_and_lexically_normalized_without_io) with a relative path while the process CWD no longer exists or current_dir() fails for OS reasons.","commonSituations":"Running the binary from a directory that was deleted after launch; launching from a removed tmp dir; containers/chroots with no accessible CWD; passing a relative credential path in scripts launched with unusual working directories.","solutions":["Pass an absolute path to resolve_external_credential_path instead of a relative one","Restore or recreate the directory the process was started from, or chdir to a valid directory before calling","Check that the process user has permission to stat the current directory","Wrap the call in error handling that surfaces the underlying io::Error for diagnosis"],"exampleFix":"// before\nresolve_external_credential_path(\"../secrets/key.toml\")\n// after\nresolve_external_credential_path(\"/home/user/project/secrets/key.toml\")","handlingStrategy":"validation","validationCode":"let p = std::path::Path::new(cred);\nif !p.is_absolute() {\n    let cwd = std::env::current_dir().map_err(|e| format!(\"cwd unavailable: {e}\"))?;\n    p = cwd.join(p);\n}","typeGuard":null,"tryCatchPattern":"match resolve_external_credential_path(p) {\n    Ok(path) => use(path),\n    Err(e) => log::error!(\"credential path resolution failed: {e}\"),\n}","preventionTips":["Always pass absolute credential paths","Resolve and cache current_dir() at startup, failing fast if unavailable","Avoid launching from directories that may be deleted (e.g. temp dirs)"],"tags":["filesystem","credentials","path-resolution"],"backgroundTag":"missing-env-var","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}