zeroclaw-labs/zeroclaw · error · anyhow::Error
email channel '{}' has oauth2 configured but no auth service
Error message
email channel '{}' has oauth2 configured but no auth service was wired in What it means
The email channel's config carries an oauth2 block, but the channel instance was built without an auth service (zeroclaw_providers::auth::AuthService) — the only component that can mint OAuth2 tokens. get_oauth2_token bails when it finds oauth2 config but no service, and this surfaces from connect_imap, so the channel cannot connect.
Source
Thrown at crates/zeroclaw-channels/src/email_channel.rs:242
.to_string();
attachments.push(zeroclaw_api::media::MediaAttachment {
file_name,
data,
mime_type: mime_str,
});
}
attachments
}
/// Attempt to obtain a bearer token via the auth service for XOAUTH2.
/// Returns `Ok(None)` when no oauth2 config is set on this channel.
async fn get_oauth2_token(&self) -> Result<Option<String>> {
let Some(ref oauth2) = self.config.oauth2 else {
return Ok(None);
};
let Some(ref auth_service) = self.auth_service else {
anyhow::bail!(
"email channel '{}' has oauth2 configured but no auth service was wired in",
self.alias
);
};
let channel_key = format!("email.{}", self.alias);
auth_service
.get_valid_email_oauth2_token(
&channel_key,
None,
&oauth2.token_url,
&oauth2.client_id,
&oauth2.scopes,
)
.await
}
/// Connect to IMAP server with TLS and authenticate
async fn connect_imap(&self) -> Result<ImapSession> {View on GitHub (pinned to 88bb9c8533)
Solutions
- Wire the auth service exactly as the orchestrator does: channel.with_auth_service(Arc::new(AuthService::from_config(&config)))
- Or stop using OAuth2: remove the oauth2 block from channels.email.<alias> and use password/credentials auth
- If embedding, mirror the orchestrator wiring rather than constructing EmailChannel bare
Example fix
// before let channel = EmailChannel::new(alias, config); // oauth2 set, no auth service → bail // after let auth = Arc::new(zeroclaw_providers::auth::AuthService::from_config(&config)); let channel = EmailChannel::new(alias, config).with_auth_service(auth);
Defensive patterns
Strategy: validation
Validate before calling
// Rust — validate wiring before connecting
if email_config.oauth2.is_some() && auth_service.is_none() {
anyhow::bail!(
"email channel '{alias}' cannot use oauth2: construct it with with_auth_service()"
);
}
email_channel.connect_imap().await?; Prevention
- Treat an oauth2 block in config as requiring with_auth_service at construction — mirror the orchestrator wiring
- Smoke-test IMAP connect at startup so wiring gaps fail fast and loudly
When it happens
Trigger: Constructing EmailChannel without calling with_auth_service (the builder leaves auth_service = None) while config.oauth2 is set, then running connect_imap. The standard orchestrator wires AuthService::from_config via with_auth_service; alternate constructors and test harnesses that skip it hit the bail.
Common situations: Embedding the email channel in a custom binary or test harness that builds channels directly; refactors that drop the with_auth_service call while oauth2 config remains in place.
Related errors
- oauth2 configured for '{}' but no auth service provided
- Email channel requires the `channel-email` feature
- Email OAuth2 profile is not OAuth-based: {profile_id}
- Email OAuth2 profile is missing token set: {profile_id}
- Email OAuth2 token refresh is in backoff for {remaining}s du
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/c76d5d88d17eb789.
Report an issue: GitHub.