zeroclaw-labs/zeroclaw · error
Jira list_projects users failed ({status}): {}
Error message
Jira list_projects users failed ({status}): {} What it means
list_projects enriches each project with assignable users; when that secondary per-project users request answers non-2xx (the else branch taken after the earlier response could not be reused), this error fires with status and truncated body. Typically 403: the token user lacks Browse Users or project-role visibility, or the deployment restricts user-picking endpoints (common on Jira Cloud after the user-API tightening); less often a plain 401.
Source
Thrown at crates/zeroclaw-tools/src/jira_tool.rs:488
})?;
let users: Vec<Value> = if users_resp.status().is_success() {
users_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 users response"
);
anyhow::Error::msg(format!(
"Failed to parse Jira list_projects users response: {e}"
))
})?
} else {
let status = users_resp.status();
let text = users_resp.text().await.unwrap_or_default();
anyhow::bail!(
"Jira list_projects users failed ({status}): {}",
crate::util_helpers::truncate_with_ellipsis(&text, MAX_ERROR_BODY_CHARS)
);
};
let mut set: tokio::task::JoinSet<(usize, anyhow::Result<Value>)> =
tokio::task::JoinSet::new();
let mut statuses_results = vec![json!([]); keys.len()];
for (i, key) in keys.iter().enumerate() {
if set.len() >= STATUS_CONCURRENCY {
let Some(Ok((idx, result))) = set.join_next().await else {
continue;
};
statuses_results[idx] = result.map_err(|e| {
::zeroclaw_log::record!(
ERROR,
::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)View on GitHub (pinned to 88bb9c8533)
Solutions
- Check the status: 403 -> raise the bot's permission (Browse Users / site access) or accept that user enrichment cannot run
- If on Cloud and the body mentions deprecated or restricted user APIs, upgrade zeroclaw-tools for the current endpoint
- Reproduce outside the agent: curl -u email:token "$BASE/rest/api/3/users/search" (or the assignable-users endpoint for one project)
- If user data is not needed, tolerate the failure and report the project list without enrichment once permissions cannot be changed
Defensive patterns
Strategy: try-catch
Type guard
fn is_jira_users_http_failure(err: &anyhow::Error) -> bool {
err.to_string()
.starts_with("Jira list_projects users failed (")
} Try / catch
match jira.execute(list_projects_args).await {
Ok(res) => res,
Err(e) if is_jira_users_http_failure(&e) => {
// the project list itself succeeded server-side; only the
// per-project users fetch was denied (usually 403).
// Retry with an elevated token or report the permission gap.
report_permission_gap(&e)
}
Err(e) => return Err(e),
} Prevention
- Grant the bot Browse Users or project-role membership up front
- Audit which Jira user endpoints the deployment allows for token auth
- Treat user-enrichment failures as a permission gap, not a data problem
- Reproduce with curl before changing permissions
When it happens
Trigger: list_projects where the main project list succeeds but the follow-up per-project users fetch is denied - a bot without Browse Users rights, a Cloud site restricting user endpoints for API-token auth, or a proxy stripping the Authorization header from just that path.
Common situations: Least-privilege bot accounts; Jira Cloud's GDPR-era user API restrictions returning 403 for Basic-auth tokens; Server/DC PATs without elevated rights; partial outages of the Jira user service.
Related errors
- Jira comment_ticket failed ({status}): {}
- Jira get_ticket failed ({status}): {}
- Jira search_tickets failed ({status}): {}
- Jira list_projects failed ({status}): {}
- statuses request returned {}
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/9786e44bae73d161.
Report an issue: GitHub.