zeroclaw-labs/zeroclaw · error · anyhow::Error
Gmail OAuth token is not configured
Error message
Gmail OAuth token is not configured
What it means
Thrown by GmailPushChannel::register_watch before any HTTP traffic: it clones GmailPushConfig.oauth_token, finds it an empty string, and bails. The token is the oauth_token field of the [channels.gmail.<alias>] block in config.toml (serde default is empty, and the field is marked #[secret]), so a channel can be constructed and even enabled with no credential at all. This is a pure configuration error, not a Google-side or network failure.
Source
Thrown at crates/zeroclaw-channels/src/gmail_push.rs:193
alias: alias.into(),
peer_resolver,
http,
last_history_id: Arc::new(Mutex::new(0)),
tx: Arc::new(Mutex::new(None)),
}
}
/// Register a Gmail watch subscription via `POST /gmail/v1/users/me/watch`.
pub async fn register_watch(&self) -> Result<WatchResponse> {
let token = self.config.oauth_token.clone();
if token.is_empty() {
::zeroclaw_log::record!(
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
.with_outcome(::zeroclaw_log::EventOutcome::Failure),
"Gmail OAuth token is not configured"
);
anyhow::bail!("Gmail OAuth token is not configured");
}
let body = serde_json::json!({
"topicName": self.config.topic,
"labelIds": self.config.label_filter,
});
let resp = self
.http
.post("https://gmail.googleapis.com/gmail/v1/users/me/watch")
.bearer_auth(&token)
.json(&body)
.send()
.await?;
if !resp.status().is_success() {
let status = resp.status();
let text = resp.text().await.unwrap_or_default();View on GitHub (pinned to 88bb9c8533)
Solutions
- Set a valid Gmail OAuth2 access token under [channels.gmail.<alias>] oauth_token in config.toml (needs the gmail.readonly scope for watch registration)
- If the token is injected from an environment variable or secret store, verify that source resolves non-empty before the channel is constructed
- If the channel should not run, keep enabled = false or remove the [channels.gmail.<alias>] block so the orchestrator never instantiates it
- Add a fail-fast startup check: any enabled channel whose oauth_token is empty should abort startup with a clear message instead of failing later inside register_watch
Example fix
# before (config.toml) [channels.gmail.main] enabled = true topic = "projects/my-project/topics/gmail-push" # after [channels.gmail.main] enabled = true topic = "projects/my-project/topics/gmail-push" oauth_token = "ya29.a0AfB..." # Gmail OAuth2 access token
Defensive patterns
Strategy: validation
Validate before calling
// Before enabling the channel / calling register_watch:
if config.oauth_token.trim().is_empty() {
anyhow::bail!("refusing to start gmail channel '{}': oauth_token is empty", alias);
}
channel.register_watch().await?; Try / catch
match channel.register_watch().await {
Ok(watch) => { /* store history_id */ }
Err(e) if e.to_string().contains("Gmail OAuth token is not configured") => {
// configuration defect: do not retry; surface to operator
return Err(e.context("set [channels.gmail.<alias>] oauth_token in config.toml"));
}
Err(e) => Err(e),
} Prevention
- Treat an enabled channel with an empty oauth_token as a startup error, not a runtime one — validate at boot
- Keep secrets out of git: source oauth_token via your secret store and assert it resolves before constructing GmailPushChannel
- Never construct production channels from GmailPushConfig::default() — it ships an empty token by design
When it happens
Trigger: Calling register_watch() directly, or letting Channel::listen / the orchestrator start a channel, when config.oauth_token.is_empty() — e.g. GmailPushConfig::default(), a [channels.gmail.<alias>] block that sets enabled = true and topic but omits oauth_token, or a token sourced from an env/secret variable that resolved to an empty string at construction time.
Common situations: Operator enables the Gmail push channel before finishing OAuth setup; config.toml generated from a template with the secret left blank; the env var feeding oauth_token missing in systemd/containers; unit tests constructing GmailPushChannel::new(GmailPushConfig::default(), ...) and touching register_watch.
Related errors
- Gmail OAuth token is not configured for sending
- Gmail Push channel requires the `channel-email` feature
- cloud_ops.iac_tools must not be empty when cloud_ops is enab
- gateway.path_prefix contains invalid character '{bad}'; only
- risk_profiles.{profile_alias}.shell_env_passthrough[{i}] is
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/6f88de9be6edce79.
Report an issue: GitHub.