zeroclaw-labs/zeroclaw · error

empty bearer token

Error message

empty bearer token

What it means

NevisAuthProvider::validate_token rejects the call before any network or signature work when the token string is empty (nevis.rs:137-139). It is a fail-fast input guard: it always fires immediately and never contacts the Nevis instance. Hitting it means your code extracted no bearer token from the request but still called the validator.

Solutions

  1. Reject requests whose Authorization header is missing or has no token before calling validate_token (return 401 early)
  2. Check token.trim().is_empty() in your middleware and log which header name was inspected
  3. Verify your header parsing strips the 'Bearer ' scheme and uses the same header name your clients send

Example fix

// before
let token = auth_header.unwrap_or_default();
let identity = provider.validate_token(&token).await?;

// after
let token = auth_header
    .and_then(|h| h.strip_prefix("Bearer "))
    .map(str::trim)
    .filter(|t| !t.is_empty())
    .ok_or_else(|| anyhow::anyhow!("missing bearer token"))?;
let identity = provider.validate_token(token).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn extract_bearer(header: Option<&str>) -> Option<&str> {
    header?
        .strip_prefix("Bearer ")
        .map(str::trim)
        .filter(|t| !t.is_empty())
}

Try / catch

If validate_token still errors, match on err.to_string().contains("empty bearer token") and return 401 immediately — never retry and never treat it as an IdP outage.

Prevention

When it happens

Trigger: Calling validate_token with an empty string — typically because the Authorization header was missing, used a different scheme or header name, or the 'Bearer ' prefix stripping left an empty value; tests pass an unset env var or String::new().

Common situations: Gateway middleware forwards an empty string when the Authorization header is absent; client sends the token in x-api-key instead; config template ships an empty token value; integration tests forget to inject the token.

Related errors


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

Appendix: source

Thrown at crates/zeroclaw-runtime/src/security/nevis.rs:138

        Ok(Self {
            instance_url,
            realm,
            client_id,
            client_secret,
            validation_mode,
            jwks_url,
            require_mfa,
            session_timeout: Duration::from_secs(session_timeout_secs),
            http_client,
        })
    }

    /// Validate a bearer token and resolve the caller's identity.
    /// Returns `NevisIdentity` on success, or an error if the token is invalid,
    /// expired, or MFA requirements are not met.
    pub async fn validate_token(&self, token: &str) -> Result<NevisIdentity> {
        if token.is_empty() {
            bail!("empty bearer token");
        }

        let identity = match self.validation_mode {
            TokenValidationMode::Local => self.validate_token_local(token).await?,
            TokenValidationMode::Remote => self.validate_token_remote(token).await?,
        };

        if self.require_mfa && !identity.mfa_verified {
            bail!(
                "MFA is required but user '{}' has not completed MFA verification",
                crate::security::redact(&identity.user_id)
            );
        }

        let now = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_secs();

View on GitHub (pinned to 88bb9c8533)