zeroclaw-labs/zeroclaw · error
oauth2 configured for '{}' but no auth service provided
Error message
oauth2 configured for '{}' but no auth service provided What it means
The IMAP connection was configured for OAuth2 (cfg.oauth2 is Some) but imap_connect was called with auth_service: None, so there is no way to mint an XOAUTH2 bearer token. The email tool's contract is: an oauth2 section in the EmailConfig requires an AuthService that can call get_valid_email_oauth2_token; when it is absent the connection is refused before any SASL authentication is attempted (email_imap.rs:99-116).
Source
Thrown at crates/zeroclaw-tools/src/email_imap.rs:112
let stream = TlsStreamTolerant(raw);
let mut client = async_imap::Client::new(stream);
client.read_response().await.context("no IMAP greeting")?;
if let Some(oauth2_cfg) = &cfg.oauth2 {
let token = if let Some(svc) = auth_service {
let channel_key = format!("email.{}", alias);
svc.get_valid_email_oauth2_token(
&channel_key,
None,
&oauth2_cfg.token_url,
&oauth2_cfg.client_id,
&oauth2_cfg.scopes,
)
.await?
.ok_or_else(|| anyhow::Error::msg(format!("no OAuth2 token available for {}", alias)))?
} else {
anyhow::bail!(
"oauth2 configured for '{}' but no auth service provided",
alias
);
};
struct XOAuth2 {
user: String,
token: String,
}
impl async_imap::Authenticator for XOAuth2 {
type Response = String;
fn process(&mut self, _: &[u8]) -> String {
format!("user={}\x01auth=Bearer {}\x01\x01", self.user, self.token)
}
}
client
.authenticate(
"XOAUTH2",View on GitHub (pinned to 88bb9c8533)
Solutions
- Wire the AuthService into the component that calls imap_connect so auth_service is Some whenever oauth2 is configured.
- If password authentication is intended, remove the oauth2 section from the email alias config so the client.login branch (email_imap.rs:139-143) is taken.
- Audit the tool factory: ensure the email tool is constructed with the same Arc<AuthService> used by other channels.
- For dev environments without OAuth2, point the alias at a password-based test account.
Example fix
// before let session = imap_connect(&cfg, None, "gmail").await?; // cfg.oauth2 is Some -> bail: oauth2 configured for 'gmail' but no auth service provided // after let auth = Arc::new(zeroclaw_providers::auth::AuthService::new(/* provider registry */)); let session = imap_connect(&cfg, Some(&auth), "gmail").await?;
Defensive patterns
Strategy: validation
Validate before calling
// Call before imap_connect: oauth2 config demands an auth service.
fn imap_config_is_callable(cfg: &EmailConfig, auth: Option<&Arc<AuthService>>) -> Result<(), String> {
if cfg.oauth2.is_some() && auth.is_none() {
return Err("email alias has oauth2 configured but no AuthService was provided; wire the auth service or remove the oauth2 block".into());
}
Ok(())
} Type guard
fn oauth2_has_auth_service(cfg: &EmailConfig, auth: Option<&Arc<AuthService>>) -> bool {
!(cfg.oauth2.is_some() && auth.is_none())
} Try / catch
match imap_connect(&cfg, auth, alias).await {
Err(e) if e.to_string().contains("no auth service provided") => {
// configuration bug, not transient: fail fast with a config-fix hint;
// do not retry
}
r => r,
} Prevention
- Treat oauth2 config sections and AuthService wiring as one unit in code review
- Add a startup config check that fails boot when any email alias has oauth2 but no auth service
- Cover this path with a unit test so refactors cannot silently pass None
When it happens
Trigger: Calling imap_connect(&cfg, None, alias) where cfg has an [email.<alias>.oauth2] block (token_url, client_id, scopes). Typically the runtime constructs the email tool without wiring the shared zeroclaw_providers auth::AuthService dependency, or a test/embedded deployment builds EmailConfig from config but skips auth service initialization.
Common situations: Adding an oauth2 section to email config without rebuilding the tool pipeline that provides the auth service; running a minimal test harness that instantiates the email tool standalone; partial refactors that pass Option::None for the auth service; Gmail/Office365 accounts where password login is disabled and XOAUTH2 is mandatory.
Related errors
- email channel '{}' has oauth2 configured but no auth service
- Email OAuth2 profile is not OAuth-based: {profile_id}
- Nevis token_validation is 'local' but no jwks_url is configu
- gateway registration failed ({status}): {err}
- matrix: {reason} Cannot auto-recover because channels.matrix
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/5abf62f9601eb730.
Report an issue: GitHub.