Hmbown/CodeWhale · error

Codewhale-owned OAuth credentials at

Error message

Codewhale-owned OAuth credentials at {} have no usable entry. Run `{hint}` again.

What it means

Codewhale manages OAuth credentials in a shared config file keyed by provider and scope. After the file was found, `select_entry` returned no entry usable for the requested provider, meaning the file exists but contains no matching OAuth entry. The library treats this as a corrupt/incomplete Codewhale-owned credential store and asks the user to re-run the login hint command.

Solutions

  1. Re-run the login command shown by `{hint}` (the codewhale OAuth login hint) to recreate the credential entry.
  2. Check the file at the printed path and verify it contains an entry for the requested provider.
  3. Back up and remove the credentials file, then log in again to regenerate it.
  4. Confirm the provider name passed to the loader matches one stored in the file.

Example fix

// before (stale file with no matching entry)
codewhale oauth login --provider anthropic
// after (regenerate entry for the provider actually used)
rm ~/.codewhale/oauth-credentials.json
codewhale oauth login --provider openai
Defensive patterns

Strategy: validation

Validate before calling

use std::path::Path;
fn has_usable_entry(path: &Path, provider: &str) -> bool {
    std::fs::read_to_string(path).ok()
        .map(|s| s.contains(provider))
        .unwrap_or(false)
}
if !has_usable_entry(&cred_path, provider) {
    eprintln!("run the oauth login hint first");
    return;
}

Type guard

fn credentials_file_exists(path: &std::path::Path) -> bool {
    path.is_file()
}

Try / catch

match load_codewhale_oauth_entry(provider) {
    Ok(entry) => use_entry(entry),
    Err(e) if e.to_string().contains("no usable entry") => {
        prompt_relogin(provider);
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling the credential-loading path in crates/tui/src/oauth.rs (around line 2243) when the Codewhale OAuth config file exists at `path` but `select_entry(provider, &mut file)` finds no entry matching the provider/scope.

Common situations: The credentials file was hand-edited or truncated, a previous login saved credentials for a different provider, the stored entry was removed by a logout/cleanup, or a schema/version change left the file without the expected entry shape.

Related errors


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

Appendix: source

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

fn get_owned_credentials_locked<F>(
    provider: OAuthProvider,
    store: &codewhale_config::XaiOAuthCredentialStore,
    name: &str,
    refresh_access: F,
) -> Result<OwnedOAuthCredentials>
where
    F: FnOnce(&str, &str, &str) -> Result<OAuthTokenMaterial>,
{
    let hint = oauth_provider_params(provider).relogin_hint;
    let path = store.path_for(name)?;
    let mut file = load_owned_auth_file_from_store(store, name)?.ok_or_else(|| {
        anyhow::anyhow!(
            "Codewhale-owned OAuth credentials were not found at {}. Run `{hint}` again.",
            codewhale_config::quote_os_path(&path)
        )
    })?;
    let (scope, mut entry) = select_entry(provider, &mut file).ok_or_else(|| {
        anyhow::anyhow!(
            "Codewhale-owned OAuth credentials at {} have no usable entry. Run `{hint}` again.",
            codewhale_config::quote_os_path(&path)
        )
    })?;

    if entry_access_token_is_fresh(&entry) {
        let token = entry
            .access_token
            .clone()
            .filter(|t| !t.trim().is_empty())
            .context("OAuth access token is empty")?;
        return Ok(credentials_from_entry(provider, &scope, &entry, token));
    }

    let refresh = entry
        .refresh_token
        .as_deref()
        .filter(|t| !t.trim().is_empty())

View on GitHub (pinned to 73e0f67d83)