Hmbown/CodeWhale · error
external credential owner does not match provider
Error message
external credential owner does not match provider {} What it means
When granting an external credential file access, the code computes an expected (consent_provider, source) pair for the provider being authorized and compares it with the pair attached to the consent record. This ensure! fires when the recorded consent's owner/origin does not match the provider it is being applied to, preventing a credential consent granted for one provider from being reused for another.
Solutions
- Regenerate the consent for the correct provider instead of reusing the mismatched record
- Remove the stale consent entry from the config and re-run the grant flow for this provider
- Verify the provider argument matches the provider the credential file/consent was actually created for
Example fix
// before apply_external_credential_consent(consent_for(ApiProvider::Xai), ApiProvider::OpenaiCodex)?; // after let consent = consent_for(ApiProvider::OpenaiCodex); // consent owner must match target apply_external_credential_consent(consent, ApiProvider::OpenaiCodex)?;
Defensive patterns
Strategy: try-catch
Validate before calling
// verify consent owner matches target provider before applying anyhow::ensure!(consent.provider == target_provider, "consent owner mismatch");
Try / catch
match apply_consent(consent, provider) {
Err(e) if e.to_string().starts_with("external credential owner does not match") => {
regrant_consent_for(provider);
}
other => other?,
} Prevention
- Never copy consent entries between providers in migrations
- Re-run the grant flow per provider instead of reusing records
- Validate consent.provider immediately after loading from disk
When it happens
Trigger: Applying or persisting an external-credential consent whose (consent_provider, source) tuple differs from the expected pair for the given ApiProvider — e.g. replaying a stored consent under a different provider, or a config file whose consent fields were hand-edited.
Common situations: Migrating or copying consent entries between providers; hand-editing the config and moving a consent block under the wrong provider; a bug writing the consent record with the wrong provider field.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- external credential consent for
- external credential consent is scoped to provider
- external credential consent path for
- external credential consent path for
- external credential consent source mismatch for
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/9a5e329a43bb3b39.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/config.rs:12849
consent_provider: codewhale_config::ProviderKind,
source: codewhale_config::ExternalCredentialSource,
path: &Path,
) -> Result<PathBuf> {
let expected = match provider {
ApiProvider::OpenaiCodex => (
codewhale_config::ProviderKind::OpenaiCodex,
codewhale_config::ExternalCredentialSource::CodexCli,
),
ApiProvider::Xai => (
codewhale_config::ProviderKind::Xai,
codewhale_config::ExternalCredentialSource::GrokCli,
),
_ => anyhow::bail!(
"{} has no supported external credential owner",
provider.as_str()
),
};
anyhow::ensure!(
(consent_provider, source) == expected,
"external credential owner does not match provider {}",
provider.as_str()
);
let path = codewhale_config::resolve_external_credential_path(path)?;
let path_value = path.to_str().context(
"external credential path cannot be persisted losslessly because it is not valid UTF-8",
)?;
validate_external_credential_before_consent(consent_provider, source, &path).with_context(
|| {
format!(
"no usable {} credential was found, so read-only consent was not saved",
source.owner_label()
)
},
)?;
let config_path = match config_path {
Some(path) => path.to_path_buf(),View on GitHub (pinned to 73e0f67d83)