{"record":{"id":"8f1558f798e702a0","repo":"Hmbown/CodeWhale","slug":"external-credential-access-is-disabled-for","errorCode":null,"errorMessage":"external credential access is disabled for {}","messagePattern":"external credential access is disabled for (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/config/src/external_credentials.rs","lineNumber":402,"sourceCode":"            source,\n            path,\n            consent_version: EXTERNAL_CREDENTIAL_CONSENT_VERSION,\n        }\n    }\n\n    /// Validate that this record is a current read-only consent for one exact\n    /// provider/source/path tuple without minting an I/O capability.\n    ///\n    /// This is intentionally side-effect free so inventory and picker surfaces\n    /// can acknowledge dormant consent without inspecting the external file.\n    pub fn validate_read_scope(\n        &self,\n        provider: ProviderKind,\n        source: ExternalCredentialSource,\n        resolved_path: &Path,\n    ) -> Result<()> {\n        if self.access == ExternalCredentialAccess::Disabled {\n            bail!(\n                \"external credential access is disabled for {}\",\n                provider.as_str()\n            );\n        }\n        if self.access == ExternalCredentialAccess::Managed {\n            bail!(\n                \"managed external credential access is unsupported for {}; no schema-safe preservation adapter is available\",\n                provider.as_str()\n            );\n        }\n        if self.consent_version != EXTERNAL_CREDENTIAL_CONSENT_VERSION {\n            bail!(\n                \"external credential consent for {} uses unsupported version {}; revoke and consent again\",\n                provider.as_str(),\n                self.consent_version\n            );\n        }\n        if self.provider != provider.as_str() {","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/0c42157ee52f9d55af2b506d71b46249910f77d3/crates/config/src/external_credentials.rs#L384-L420","documentation":"validate_read_scope() checks that a stored external-credential consent actually grants the access being attempted. Access == ExternalCredentialAccess::Disabled means consent exists but is explicitly switched off (revoked/denied), so any read attempt for that provider bails without touching the file — inventory surfaces can still show the dormant consent.","triggerScenarios":"A consent record for the provider exists in config with access = \"disabled\" and code paths call validate_read_scope(provider, source, path) intending to read the credential.","commonSituations":"User revoked consent (or a security policy set it to disabled) and later selects that provider/credential again; consent deliberately parked as disabled during audits; tooling that lists credentials then attempts reads without checking the access level.","solutions":["Re-enable access by consenting again for that provider/source/path rather than editing the record by hand","If the denial is intentional, stop selecting that external credential and use another auth source (env var, command, secret_id)"],"exampleFix":"# before: consent record with access = \"disabled\", provider still selected\n\n# after: revoke and consent again\n$ codewhale credentials revoke <provider> && codewhale credentials consent <provider> --path /keys/acme.env","handlingStrategy":"try-catch","validationCode":"// Check the access level before attempting a read:\nif consent.access == ExternalCredentialAccess::Disabled {\n    // surface 're-consent required' in the UI instead of calling read APIs\n}","typeGuard":"fn grants_read(access: ExternalCredentialAccess) -> bool {\n    matches!(access, ExternalCredentialAccess::ReadOnly)\n}","tryCatchPattern":"match consent.validate_read_scope(provider, source, &path) {\n    Ok(()) => read_external_credential(&path),\n    Err(e) if e.to_string().contains(\"access is disabled\") => {\n        // prompt the user to consent again; do not silently switch auth source\n        prompt_reconsent(provider)\n    }\n    Err(e) => Err(e),\n}","preventionTips":["Filter disabled consents out of credential pickers before reads","Re-consent after revoking instead of hand-editing the access field","Audit consent records periodically for access = \"disabled\" leftovers"],"tags":["security","consent","credentials","authorization"],"backgroundTag":"consent-access-denied","analyzedSha":"0c42157ee52f9d55af2b506d71b46249910f77d3","analyzedAt":"2026-08-20T21:50:45.477Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}