zeroclaw-labs/zeroclaw · error

Jira list_projects failed ({status}): {}

Error message

Jira list_projects failed ({status}): {}

What it means

GET {base_url}/rest/api/{2|3}/project returned non-2xx during list_projects; the truncated body is inlined. This is usually pure access failure: 401 wrong email/token pair (Cloud) or wrong PAT (Server/DC), 403 the token user may browse no projects, or 404 from a base_url that is not a Jira site root (edge host, wrong product domain, proxy).

Source

Thrown at crates/zeroclaw-tools/src/jira_tool.rs:424

        let req = self
            .http
            .get(&url)
            .timeout(std::time::Duration::from_secs(self.timeout_secs));
        let resp = self.authenticated(req).send().await.map_err(|e| {
            ::zeroclaw_log::record!(
                ERROR,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({"error": format!("{}", e)})),
                "jira: Jira list_projects request failed"
            );
            anyhow::Error::msg(format!("Jira list_projects request failed: {e}"))
        })?;

        let status = resp.status();
        if !status.is_success() {
            let text = resp.text().await.unwrap_or_default();
            anyhow::bail!(
                "Jira list_projects failed ({status}): {}",
                crate::util_helpers::truncate_with_ellipsis(&text, MAX_ERROR_BODY_CHARS)
            );
        }

        let projects: Vec<Value> = resp.json().await.map_err(|e| {
            ::zeroclaw_log::record!(
                ERROR,
                ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)
                    .with_outcome(::zeroclaw_log::EventOutcome::Failure)
                    .with_attrs(::serde_json::json!({"error": format!("{}", e)})),
                "jira: Failed to parse Jira list_projects response"
            );
            anyhow::Error::msg(format!("Failed to parse Jira list_projects response: {e}"))
        })?;

        let keys: Vec<String> = projects
            .iter()

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Call the myself action first: a 401 there means credentials, a 200 means this is a permission or URL problem
  2. Re-create the API token and update the secret store
  3. Confirm base_url resolves to the Jira site root and nothing else
  4. Grant the token user Browse Projects in at least one project

Example fix

// before: secret loaded with a trailing newline -> Jira 401
let token = std::fs::read_to_string("token.txt")?;

// after: trim - whitespace-padded credentials always fail
let token = std::fs::read_to_string("token.txt")?.trim().to_string();
Defensive patterns

Strategy: validation

Validate before calling

// one-time preflight before relying on list_projects
async fn jira_credentials_ok(jira: &JiraTool) -> bool {
    jira.execute(serde_json::json!({"action": "myself"}))
        .await
        .is_ok()
}

Type guard

fn is_jira_list_projects_http_failure(err: &anyhow::Error) -> bool {
    err.to_string().starts_with("Jira list_projects failed (")
}

Try / catch

match jira.execute(list_projects_args).await {
    Ok(res) => res,
    Err(e) if is_jira_list_projects_http_failure(&e) => {
        let msg = e.to_string();
        if msg.starts_with("Jira list_projects failed (401") {
            halt_and_reauth(&msg) // every other action would 401 too
        } else {
            return Err(e)
        }
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: list_projects with a revoked Atlassian API token; a newly created PAT not yet propagated; a user with no Browse Projects permission anywhere; base_url pointing at a Confluence domain or portal so /rest/api/*/project 404s.

Common situations: Tokens rotated overnight by security policy; site renames invalidating a cached base_url; CI secrets carrying trailing newlines that corrupt Basic auth; Atlassian answering 401 with a 'basic auth with API token' hint when an account password was supplied instead of a token.

Related errors


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