zeroclaw-labs/zeroclaw · error

jira.api_token must be set (or JIRA_API_TOKEN env var) when

Error message

jira.api_token must be set (or JIRA_API_TOKEN env var) when jira.enabled = true

What it means

Thrown by Config::validate when jira.enabled = true and no API token is available from either jira.api_token in config or the JIRA_API_TOKEN environment variable (both checked after trimming). The check is an either/or: config value wins the first branch, the env var is the fallback, and only when both are empty does it bail. This lets deployments keep the secret out of the config file.

Source

Thrown at crates/zeroclaw-config/src/schema.rs:22073

            if !r.is_empty() && !matches!(r.as_str(), "us" | "eu" | "ap" | "br" | "au") {
                anyhow::bail!(
                    "tunnel.pinggy.region must be one of: us, eu, ap, br, au (or omitted for auto)"
                );
            }
        }

        // Jira
        if self.jira.enabled {
            if self.jira.base_url.trim().is_empty() {
                anyhow::bail!("jira.base_url must not be empty when jira.enabled = true");
            }
            if self.jira.api_token.trim().is_empty()
                && std::env::var("JIRA_API_TOKEN")
                    .unwrap_or_default()
                    .trim()
                    .is_empty()
            {
                anyhow::bail!(
                    "jira.api_token must be set (or JIRA_API_TOKEN env var) when jira.enabled = true"
                );
            }
            let valid_actions = [
                "get_ticket",
                "search_tickets",
                "comment_ticket",
                "list_projects",
                "myself",
                "list_transitions",
                "transition_ticket",
                "create_ticket",
            ];
            for action in &self.jira.allowed_actions {
                if !valid_actions.contains(&action.as_str()) {
                    anyhow::bail!(
                        "jira.allowed_actions contains unknown action: '{}'. \
                         Valid: get_ticket, search_tickets, comment_ticket, list_projects, myself, list_transitions, transition_ticket, create_ticket",

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Export JIRA_API_TOKEN in the environment of the process that loads the config (systemd Environment=, .env, shell profile)
  2. Or set jira.api_token in config.toml if file-based secrets are acceptable for your setup
  3. Create the token at id.atlassian.com → Security → API token if none exists yet
  4. Verify with `printenv JIRA_API_TOKEN` in the exact launch context of the daemon

Example fix

# before
[jira]
enabled = true
base_url = "https://yourcompany.atlassian.net"
# no token anywhere

# after (option A: env var)
# export JIRA_API_TOKEN=ATATT3fP...
# after (option B: config)
[jira]
enabled = true
base_url = "https://yourcompany.atlassian.net"
api_token = "ATATT3fP..."
Defensive patterns

Strategy: validation

Validate before calling

fn jira_token_available(j: &JiraConfig) -> bool {
    !j.enabled
        || !j.api_token.trim().is_empty()
        || !std::env::var("JIRA_API_TOKEN").unwrap_or_default().trim().is_empty()
}
// fail fast at startup, not at first Jira call:
if !jira_token_available(&cfg.jira) {
    anyhow::bail!("JIRA_API_TOKEN missing in this environment");
}

Try / catch

match cfg.validate() {
    Err(e) if e.to_string().starts_with("jira.api_token") => {
        eprintln!("Provide jira.api_token or export JIRA_API_TOKEN in the service environment");
        std::process::exit(78); // EX_CONFIG
    }
    other => other?,
}

Prevention

When it happens

Trigger: Enabling Jira with api_token unset/blank while JIRA_API_TOKEN is also unset or set to empty/whitespace in the process environment; CI or systemd units that strip environment variables; running under a service manager that does not pass user env vars.

Common situations: Token configured in the interactive shell but the daemon is started from a different environment (launchd, systemd, Docker); token removed from config for security but env var wiring forgotten; expired/rotated token deleted without replacing either source.

Related errors


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