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

  1. Wire the AuthService into the component that calls imap_connect so auth_service is Some whenever oauth2 is configured.
  2. 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.
  3. Audit the tool factory: ensure the email tool is constructed with the same Arc<AuthService> used by other channels.
  4. 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

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


AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23). Data as JSON: /api/errors/5abf62f9601eb730. Report an issue: GitHub.