risingwavelabs/risingwave · error · anyhow::Error

plan "{}" format illegal

Error message

plan "{}" format illegal

What it means

parse_plan splits the reschedule plan into per-fragment strings and matches each against a regex to extract fragment id and worker changes. This error means a fragment plan chunk did not match the expected format, so it cannot be parsed.

Source

Thrown at src/ctl/src/cmd_impl/meta/reschedule.rs:154

    }

    Ok(())
}

// It will match formats like `1:[1:+1,2:-1,3:1];2:[1:1,2:1]`, indicating which workers' actors need to change in quantity for each fragment.
fn parse_plan(mut plan: String) -> Result<HashMap<u32, PbWorkerReschedule>> {
    let mut reschedules = HashMap::new();
    let regex = Regex::new(r"^(\d+):\[((?:\d+:[+-]?\d+,?)+)]$")?;
    plan.retain(|c| !c.is_whitespace());

    for fragment_reschedule_plan in plan.split(';') {
        if fragment_reschedule_plan.is_empty() {
            continue;
        }

        let captures = regex
            .captures(fragment_reschedule_plan)
            .ok_or_else(|| anyhow!("plan \"{}\" format illegal", fragment_reschedule_plan))?;

        let fragment_id = captures
            .get(1)
            .and_then(|mat| mat.as_str().parse::<u32>().ok())
            .ok_or_else(|| anyhow!("plan \"{}\" does not have a valid fragment id", plan))?;

        let worker_changes: Vec<&str> = captures[2].split(',').collect();

        let mut worker_actor_diff = HashMap::new();
        for worker_change in &worker_changes {
            let (worker_id, count) = worker_change.split(':').collect_tuple::<(_, _)>().unwrap();
            let worker_id = worker_id.parse().unwrap();
            let count = count.parse().unwrap();

            if let Some(dup_change) = worker_actor_diff.insert(worker_id, count) {
                anyhow::bail!(
                    "duplicate worker id {worker_id} in plan, prev {worker_id} -> {dup_change}",
                );

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Reformat the plan to the expected per-fragment syntax the regex expects
  2. Copy the plan directly from the meta tooling output instead of retyping it
  3. Match risectl and meta versions so plan formats agree
Defensive patterns

Strategy: validation

Validate before calling

if !plan.lines().all(|l| l.trim().is_empty() || FRAGMENT_RE.is_match(l.trim())) {
    return Err(anyhow!("plan lines must match fragment plan format"));
}

Prevention

When it happens

Trigger: Passing a reschedule plan string whose fragment sections deviate from the expected pattern (missing brackets, wrong separators, whitespace or extra text where the regex expects 'fragment_id [worker:count,...]').

Common situations: Hand-editing plan text copied from meta logs; older/newer plan formats from mismatched risectl and meta versions; truncation when pasting multi-line plans.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/04c25d6dcec46c1f. Report an issue: GitHub.