zeroclaw-labs/zeroclaw · error

transition_ticket requires exactly one of transition_id or t

Error message

transition_ticket requires exactly one of transition_id or transition_name

What it means

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.

Source

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

                        None
                    }
                });
                match found {
                    Some(id) => id,
                    None => {
                        let available: Vec<&str> = transitions
                            .iter()
                            .filter_map(|t| t["name"].as_str())
                            .collect();
                        anyhow::bail!(
                            "Transition '{name}' not found for {issue_key}. Available: {}",
                            available.join(", ")
                        );
                    }
                }
            }
            _ => {
                anyhow::bail!(
                    "transition_ticket requires exactly one of transition_id or transition_name"
                );
            }
        };

        let ver = self.api_version();
        let url = format!(
            "{}/rest/api/{}/issue/{}/transitions",
            self.base_url, ver, issue_key
        );
        let body = json!({ "transition": { "id": resolved_id } });

        let req = self
            .http
            .post(&url)
            .json(&body)
            .timeout(std::time::Duration::from_secs(self.timeout_secs));
        let resp = self.authenticated(req).send().await.map_err(|e| {

View on GitHub (pinned to 88bb9c8533)

Solutions

  1. Pass a non-empty transition_id or transition_name (trim whitespace first).
  2. If your caller has optional values, default to empty-string handling: pass None rather than "" so intent is explicit.
  3. Validate arguments before dispatching the tool call (see defense below).
  4. When both are known, pass only transition_id to make intent unambiguous.

Example fix

// before
jira.transition_ticket("PROJ-42", Some(""), Some("")).await?; // -> error

// after
let name = "In Progress";
jira.transition_ticket("PROJ-42", None, Some(name)).await?;
Defensive patterns

Strategy: validation

Validate before calling

// Reject before dispatch: require exactly one selector
fn valid_transition_args(id: Option<&str>, name: Option<&str>) -> bool {
    let has_id = id.map(|s| !s.trim().is_empty()).unwrap_or(false);
    let has_name = name.map(|s| !s.trim().is_empty()).unwrap_or(false);
    has_id ^ has_name // exactly one
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Missing required parameter errors: what 'X is required' and 'the required X param is missing' mean, and how to fix them — this error's family across 27 libraries.

Related errors


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