{"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":"anyhow::Error","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/8880682c63083a91624de936797efa3ce9e498fd/crates/config/src/external_credentials.rs#L95-L131","documentation":"resolve_external_credential_path turns a user-selected credential path into an absolute logical path without touching the filesystem (consent binds to the exact logical path). For a relative path it must join the current working directory; this error wraps the OS failure of std::env::current_dir().","triggerScenarios":"Passing a relative external-credential path while the process working directory was deleted (Linux getcwd fails with ENOENT) or made unreadable (EACCES), so the OS cannot report it.","commonSituations":"A shell sitting in a directory that was rm -rf'ed; CI running from a workspace deleted mid-job; containers whose mount was removed before the command ran.","solutions":["cd into an existing directory (or restart the terminal) and retry","Pass an absolute credential path so the cwd is never consulted","If a wrapper script chdir's into a temp dir, make sure that dir still exists when the command runs"],"exampleFix":"# before (shell sitting in a deleted directory)\n$ codewhale credentials add ./key.json   # resolving external credential path: No such file or directory\n\n# after\n$ cd ~ && codewhale credentials add ~/keys/key.json","handlingStrategy":"validation","validationCode":"// Absolutize without relying on a live cwd, or verify cwd first:\nfn safe_absolute(p: &std::path::Path) -> std::io::Result<std::path::PathBuf> {\n    if p.is_absolute() {\n        Ok(p.to_path_buf())\n    } else {\n        std::env::current_dir().map(|d| d.join(p))\n    }\n}","typeGuard":null,"tryCatchPattern":"On Err, tell the user the working directory is gone and re-prompt with an absolute path; a retry only helps after the cwd exists again, so do not loop on it.","preventionTips":["Pass absolute credential paths from wrapper scripts and CI jobs","Ensure the launch directory outlives the command (never rm -rf the cwd of a running job)"],"tags":["credentials","path","cwd","filesystem"],"backgroundTag":null,"analyzedSha":"8880682c63083a91624de936797efa3ce9e498fd","analyzedAt":"2026-08-16T11:31:27.956Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}