{"record":{"id":"c69ad23af9dc1c5a","repo":"zeroclaw-labs/zeroclaw","slug":"transition-ticket-requires-exactly-one-of-transiti","errorCode":null,"errorMessage":"transition_ticket requires exactly one of transition_id or transition_name","messagePattern":"transition_ticket requires exactly one of transition_id or transition_name","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/zeroclaw-tools/src/jira_tool.rs","lineNumber":782,"sourceCode":"                        None\n                    }\n                });\n                match found {\n                    Some(id) => id,\n                    None => {\n                        let available: Vec<&str> = transitions\n                            .iter()\n                            .filter_map(|t| t[\"name\"].as_str())\n                            .collect();\n                        anyhow::bail!(\n                            \"Transition '{name}' not found for {issue_key}. Available: {}\",\n                            available.join(\", \")\n                        );\n                    }\n                }\n            }\n            _ => {\n                anyhow::bail!(\n                    \"transition_ticket requires exactly one of transition_id or transition_name\"\n                );\n            }\n        };\n\n        let ver = self.api_version();\n        let url = format!(\n            \"{}/rest/api/{}/issue/{}/transitions\",\n            self.base_url, ver, issue_key\n        );\n        let body = json!({ \"transition\": { \"id\": resolved_id } });\n\n        let req = self\n            .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| {","sourceCodeStart":764,"sourceCodeEnd":800,"githubUrl":"https://github.com/zeroclaw-labs/zeroclaw/blob/88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc/crates/zeroclaw-tools/src/jira_tool.rs#L764-L800","documentation":"Thrown by transition_ticket when neither a usable transition_id nor transition_name was supplied: the match arm falls through to the error when both are None or both are empty/whitespace-only strings. Despite the wording 'exactly one', if both are provided the non-empty transition_id silently wins; the error only fires when there is nothing to act on. No network request is made.","triggerScenarios":"Calling transition_ticket with transition_id=None and transition_name=None; passing Some(\"\") or Some(\"   \") for both parameters (empty strings are treated as absent); building args from optional JSON fields that were omitted in the tool call.","commonSituations":"LLM/tool-caller omitting both fields when invoking the jira tool action; a wrapper mapping missing request fields to empty strings instead of None; refactoring that renamed the parameters and left callers passing neither.","solutions":["Pass a non-empty transition_id or transition_name (trim whitespace first).","If your caller has optional values, default to empty-string handling: pass None rather than \"\" so intent is explicit.","Validate arguments before dispatching the tool call (see defense below).","When both are known, pass only transition_id to make intent unambiguous."],"exampleFix":"// before\njira.transition_ticket(\"PROJ-42\", Some(\"\"), Some(\"\")).await?; // -> error\n\n// after\nlet name = \"In Progress\";\njira.transition_ticket(\"PROJ-42\", None, Some(name)).await?;","handlingStrategy":"validation","validationCode":"// Reject before dispatch: require exactly one selector\nfn valid_transition_args(id: Option<&str>, name: Option<&str>) -> bool {\n    let has_id = id.map(|s| !s.trim().is_empty()).unwrap_or(false);\n    let has_name = name.map(|s| !s.trim().is_empty()).unwrap_or(false);\n    has_id ^ has_name // exactly one\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate tool-call arguments at the boundary before invoking execute.","Normalize empty strings to None so absence is explicit.","Prefer transition_id in automation; keep transition_name for interactive flows."],"tags":["jira","parameter-validation","transitions"],"backgroundTag":"missing-required-parameter","analyzedSha":"88bb9c8533fc57ed7a03e36ca7c9ed2bf8336dcc","analyzedAt":"2026-08-23T01:07:41.857Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}