{"record":{"id":"621c0891340a01d2","repo":"Hmbown/CodeWhale","slug":"external-credential-consent-is-scoped-to-provider","errorCode":null,"errorMessage":"external credential consent is scoped to provider {:?}, not {}","messagePattern":"external credential consent is scoped to provider (.+?), not (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/config/src/external_credentials.rs","lineNumber":382,"sourceCode":"                \"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() {\n            bail!(\n                \"external credential consent is scoped to provider {:?}, not {}\",\n                self.provider,\n                provider.as_str()\n            );\n        }\n        if self.source != source {\n            bail!(\n                \"external credential consent source mismatch for {} (expected {})\",\n                provider.as_str(),\n                source.as_str()\n            );\n        }\n        if !self.path.is_absolute() {\n            bail!(\n                \"external credential consent path for {} must be absolute\",\n                provider.as_str()\n            );\n        }","sourceCodeStart":364,"sourceCodeEnd":400,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/config/src/external_credentials.rs#L364-L400","documentation":"Thrown by `ExternalCredentialConsentToml::validate_read_scope` when a stored consent record names a different provider than the one whose credentials are being read. Consent grants are provider-scoped; a record created for one provider can never authorize a read for another. This is a safety check preventing a consent record from being replayed across providers.","triggerScenarios":"Calling `read_grant` (directly or via `external_credential_consent_status`) with a `provider` argument whose `as_str()` differs from the `provider` field persisted in the consent record.","commonSituations":"A consent file created for one provider (e.g. \"google\") is being consulted for another (e.g. \"openrouter\") after a config rename or copy; a user manually edited or moved a consent TOML between provider sections; provider identifier changed (e.g. legacy \"antigravity\" vs \"google\") but the old consent record remains.","solutions":["Re-run the consent flow for the provider you are actually reading so a correctly-scoped consent record is created.","Revoke the stale consent (delete the consent record) and consent again with the matching provider.","Verify the provider argument passed to read_grant/consent-status matches the provider the user actually consented to."],"exampleFix":"// before: reading google creds with an antigravity-scoped consent\nlet grant = store.read_grant(ProviderKind::Google, source, &path)?;\n// after: revoke the stale record and re-consent for google, or pass\n// the provider the consent was actually created for\nlet grant = store.read_grant(ProviderKind::Antigravity, source, &path)?;","handlingStrategy":"validation","validationCode":"fn consent_matches(consent: &ExternalCredentialConsentToml, provider: ProviderKind) -> bool {\n    consent.provider == provider.as_str()\n}","typeGuard":null,"tryCatchPattern":"match store.read_grant(provider, source, &path) {\n    Ok(grant) => use_grant(grant),\n    Err(e) if e.to_string().contains(\"scoped to provider\") => {\n        // stale consent for another provider: revoke and re-run consent flow\n        store.revoke(provider)?;\n        run_consent_flow(provider)?;\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always re-run the consent flow after changing which provider is used.","Never copy consent records between provider sections.","Remove legacy provider identifiers (e.g. antigravity) from config and re-consent under the current provider."],"tags":["config","credentials","consent","validation"],"backgroundTag":"invalid-config-value","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}