Hmbown/CodeWhale · error

external credential file not found at

Error message

external credential file not found at {}

What it means

When consuming another CLI's credential file under a read-only `ExternalCredentialReadGrant`, the file at the granted path does not exist (or is unreadable as a string), so it cannot be parsed into an `AuthFile`. The library quotes the OS path in the message to make the missing file obvious.

Solutions

  1. Run the other CLI's login first (e.g. `grok login`) so its credential file exists at the granted path.
  2. Verify the configured external credential path matches the real file (check `~/.config/<cli>/auth.json` or equivalent).
  3. Alternatively use Codewhale-owned storage via `codewhale auth xai-device` instead of importing.

Example fix

// before (config)
external_credential_path = "/home/me/.grok/auth.json"   // file absent
// after
$ grok login            # creates the auth file
codewhale ...           # import now finds it
Defensive patterns

Strategy: validation

Validate before calling

use std::path::Path;
let path = grant.path();
if !Path::new(path).is_file() {
    // run `grok login` first or fix the configured path before importing
}

Type guard

fn external_credential_exists(grant: &ExternalCredentialReadGrant) -> bool {
    std::path::Path::new(grant.path()).is_file()
}

Prevention

When it happens

Trigger: Calling `load_external_auth_file(grant)` where `crate::external_credentials::read_to_string(grant)` returns `None` — the path in the grant (`grant.path()`) has no file on disk.

Common situations: Pointing the external-credential grant at a path where `grok login` (or another CLI) never wrote its auth file; the other CLI not yet logged in; the file was deleted or lives under a different HOME; typo in the configured path.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/ceff7f2f6684c323. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/oauth.rs:1773

    store
        .write(name, &serialized, allow_replace)
        .with_context(|| {
            format!(
                "writing owned OAuth credentials to {}",
                codewhale_config::quote_os_path(&store.directory().join(name))
            )
        })?;
    #[cfg(test)]
    crate::external_credentials::record_owned_credential_write();
    Ok(())
}

/// Read-only parse of another CLI's granted credential file.
fn load_external_auth_file(
    grant: &codewhale_config::ExternalCredentialReadGrant,
) -> Result<AuthFile> {
    let Some(raw) = crate::external_credentials::read_to_string(grant)? else {
        bail!(
            "external credential file not found at {}",
            codewhale_config::quote_os_path(grant.path())
        );
    };
    parse_auth_file(&raw, grant.path())
}

fn select_entry(provider: OAuthProvider, file: &mut AuthFile) -> Option<(String, OwnedAuthEntry)> {
    // Prefer this provider's registered client-id scope when present.
    let preferred_suffix = format!("::{}", oauth_provider_params(provider).default_client_id);
    if let Some((k, v)) = file
        .iter()
        .find(|(k, e)| k.ends_with(&preferred_suffix) && entry_has_usable_secret(e))
    {
        return Some((k.clone(), v.clone()));
    }
    file.iter()
        .find(|(_, e)| entry_has_usable_secret(e))

View on GitHub (pinned to 73e0f67d83)