{"record":{"id":"28999d8f381311b3","repo":"zeroclaw-labs/zeroclaw","slug":"jira-search-tickets-failed-status","errorCode":null,"errorMessage":"Jira search_tickets failed ({status}): {}","messagePattern":"Jira search_tickets failed \\((.+?)\\): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/jira_tool.rs","lineNumber":224,"sourceCode":"                .http\n                .post(&url)\n                .json(&body)\n                .timeout(std::time::Duration::from_secs(self.timeout_secs));\n            let resp = self.authenticated(req).send().await.map_err(|e| {\n                ::zeroclaw_log::record!(\n                    ERROR,\n                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                        .with_attrs(::serde_json::json!({\"error\": format!(\"{}\", e)})),\n                    \"jira: Jira search_tickets request failed\"\n                );\n                anyhow::Error::msg(format!(\"Jira search_tickets request failed: {e}\"))\n            })?;\n\n            let status = resp.status();\n            if !status.is_success() {\n                let text = resp.text().await.unwrap_or_default();\n                anyhow::bail!(\n                    \"Jira search_tickets failed ({status}): {}\",\n                    crate::util_helpers::truncate_with_ellipsis(&text, MAX_ERROR_BODY_CHARS)\n                );\n            }\n\n            let raw: Value = resp.json().await.map_err(|e| {\n                ::zeroclaw_log::record!(\n                    ERROR,\n                    ::zeroclaw_log::Event::new(module_path!(), ::zeroclaw_log::Action::Fail)\n                        .with_outcome(::zeroclaw_log::EventOutcome::Failure)\n                        .with_attrs(::serde_json::json!({\"error\": format!(\"{}\", e)})),\n                    \"jira: Failed to parse Jira search response\"\n                );\n                anyhow::Error::msg(format!(\"Failed to parse Jira search response: {e}\"))\n            })?;\n\n            if let Some(page) = raw[\"issues\"].as_array() {\n                issues.extend(page.iter().map(shape_basic_search));","sourceCodeStart":206,"sourceCodeEnd":242,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/jira_tool.rs#L206-L242","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Paste the exact JQL into Jira's advanced search in the web UI to get its precise error","Fix quoting: wrap phrases in double quotes, escape embedded quotes, and check field names against /rest/api/3/field","If the message shows 404/410 with a 'removed' body, upgrade zeroclaw-tools - the endpoint contract changed","On 401/403 verify email + token with the myself action before debugging JQL"],"exampleFix":"// before: raw user phrase passed as JQL -> HTTP 400\n{\"action\": \"search_tickets\", \"query\": \"summary ~ ship it now\"}\n\n// after: quote the phrase so the JQL parses\n{\"action\": \"search_tickets\", \"query\": \"summary ~ \\\"ship it now\\\"\"}","handlingStrategy":"try-catch","validationCode":"// cheap JQL sanity check before the call\nfn jql_looks_valid(q: &str) -> bool {\n    let q = q.trim();\n    !q.is_empty() && q.matches('\"').count() % 2 == 0\n}","typeGuard":"fn is_jira_search_http_failure(err: &anyhow::Error) -> bool {\n    err.to_string().starts_with(\"Jira search_tickets failed (\")\n}","tryCatchPattern":"match jira.execute(search_args).await {\n    Ok(res) => res,\n    Err(e) if is_jira_search_http_failure(&e) => {\n        let msg = e.to_string();\n        if msg.contains(\"(400\") {\n            surface_jql_error(&msg) // body carries Atlassian's JQL message\n        } else if msg.contains(\"(401\") || msg.contains(\"(403\") {\n            refresh_credentials()\n        } else {\n            return Err(e)\n        }\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Quote and escape dynamic JQL fragments before invoking search_tickets","Prefer field IDs over custom-field names in stored JQL","Track Atlassian search-endpoint deprecations and keep zeroclaw-tools current","Cache /rest/api/3/field names once per session to catch renames early"],"tags":["rust","zeroclaw","jira","jql","search","http-status"],"backgroundTag":"jira-rest-api-error","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}