Hmbown/CodeWhale · error · anyhow::Error

external credential consent source mismatch for {} (expected

Error message

external credential consent source mismatch for {} (expected {})

What it means

Part of the exact-tuple validation in validate_read_scope(): the consent's stored ExternalCredentialSource must equal the source the caller is requesting (e.g. a path-based file source vs another backend). A mismatch means the grant was minted for a different acquisition mechanism and is refused before any capability is issued.

Source

Thrown at crates/config/src/external_credentials.rs:428

                provider.as_str()
            );
        }
        if self.consent_version != EXTERNAL_CREDENTIAL_CONSENT_VERSION {
            bail!(
                "external credential consent for {} uses unsupported version {}; revoke and consent again",
                provider.as_str(),
                self.consent_version
            );
        }
        if self.provider != provider.as_str() {
            bail!(
                "external credential consent is scoped to provider {:?}, not {}",
                self.provider,
                provider.as_str()
            );
        }
        if self.source != source {
            bail!(
                "external credential consent source mismatch for {} (expected {})",
                provider.as_str(),
                source.as_str()
            );
        }
        if !self.path.is_absolute() {
            bail!(
                "external credential consent path for {} must be absolute",
                provider.as_str()
            );
        }
        let normalized = resolve_external_credential_path(&self.path)?;
        if normalized != self.path {
            bail!(
                "external credential consent path for {} must be lexically normalized: {}",
                provider.as_str(),
                quote_os_path(&self.path)
            );

View on GitHub (pinned to 0c42157ee5)

Solutions

  1. Re-create the consent with the source you now actually use
  2. Align the code path and the consent: validate with the same source value the consent was created under

Example fix

# before
[[external_credentials]]
provider = "acme"
source = "keyring"   # but the caller requests the path source

# after
[[external_credentials]]
provider = "acme"
source = "path"
path = "/keys/acme.env"
Defensive patterns

Strategy: validation

Validate before calling

if consent.source != requested_source {
    // grant minted for a different acquisition mechanism; re-consent
    return Err(anyhow!("consent source mismatch: recreate for {requested_source:?}"));
}

Type guard

fn matches_source(consent: &ExternalCredentialConsent, s: ExternalCredentialSource) -> bool {
    consent.source == s
}

Try / catch

match consent.validate_read_scope(provider, source, &path) {
    Ok(()) => read_external_credential(&path),
    Err(e) if e.to_string().contains("source mismatch") => {
        reconsent_with_source(provider, source, &path).await
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: A consent record created for source = "path" is validated with source = another ExternalCredentialSource variant (or vice versa) — commonly after config copying or after a code path switched how it resolves the same provider's credential.

Common situations: Reusing a consent block when switching a provider from a file-based credential to a keyring/other source (or the reverse); edits to the source field; version changes renaming source variants so deserialized values no longer match.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@0c42157ee5 (2026-08-20). Data as JSON: /api/errors/cef1ae14ff3cfd06. Report an issue: GitHub.