zeroclaw-labs/zeroclaw · error

Jira search_tickets failed ({status}): {}

Error message

Jira search_tickets failed ({status}): {}

What it means

Thrown when the Jira Cloud search call (POST {base_url}/rest/api/3/search with JQL, page size 100) returns non-2xx; search_tickets dispatches to this v3 path whenever an email is configured. The truncated body (500 chars) is inlined. Frequent causes: 400 invalid JQL (syntax, unknown field or function), 401/403 credential problems, or 404/gone when Atlassian removes or changes the Cloud REST search endpoint.

Source

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

                .http
                .post(&url)
                .json(&body)
                .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 search_tickets request failed"
                );
                anyhow::Error::msg(format!("Jira search_tickets request failed: {e}"))
            })?;

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

            let raw: 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 search response"
                );
                anyhow::Error::msg(format!("Failed to parse Jira search response: {e}"))
            })?;

            if let Some(page) = raw["issues"].as_array() {
                issues.extend(page.iter().map(shape_basic_search));

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Paste the exact JQL into Jira's advanced search in the web UI to get its precise error
  2. Fix quoting: wrap phrases in double quotes, escape embedded quotes, and check field names against /rest/api/3/field
  3. If the message shows 404/410 with a 'removed' body, upgrade zeroclaw-tools - the endpoint contract changed
  4. On 401/403 verify email + token with the myself action before debugging JQL

Example fix

// before: raw user phrase passed as JQL -> HTTP 400
{"action": "search_tickets", "query": "summary ~ ship it now"}

// after: quote the phrase so the JQL parses
{"action": "search_tickets", "query": "summary ~ \"ship it now\""}
Defensive patterns

Strategy: try-catch

Validate before calling

// cheap JQL sanity check before the call
fn jql_looks_valid(q: &str) -> bool {
    let q = q.trim();
    !q.is_empty() && q.matches('"').count() % 2 == 0
}

Type guard

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

Try / catch

match jira.execute(search_args).await {
    Ok(res) => res,
    Err(e) if is_jira_search_http_failure(&e) => {
        let msg = e.to_string();
        if msg.contains("(400") {
            surface_jql_error(&msg) // body carries Atlassian's JQL message
        } else if msg.contains("(401") || msg.contains("(403") {
            refresh_credentials()
        } else {
            return Err(e)
        }
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: search_tickets with malformed JQL - unbalanced quotes, unescaped reserved words, or a custom field referenced by a name that does not exist in the site; an empty query string; or an Atlassian-side endpoint change where /rest/api/3/search answers 404/410 for the pinned zeroclaw-tools version.

Common situations: Agents building JQL from raw user input with unescaped double quotes; sites where custom fields were renamed after JQL templates were written; Atlassian deprecating older search endpoints so an older zeroclaw-tools build starts failing until upgraded.

Related errors


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