tinyhumansai/openhuman · error

workflow fan-out: worker {} failed: {err}

Error message

workflow fan-out: worker {} failed: {err}

What it means

Thrown by workflow_runs phase execution when, after a CollectAll fan-out, one of the per-agent worker results is an Err. Note the worker closure returns Ok(PhaseWorkerOutcome) for ordinary failures (spawn error, child ending Failed/Cancelled, wait timeout) — those surface as phase data, not this error. An Err result therefore means the worker task itself failed at the executor level, e.g. the worker future panicked or was joined abnormally; item.index is its position in the phase's agent list.

Source

Thrown at src/openhuman/agent/orchestration/workflow_runs/engine.rs:720

                    config,
                    &run,
                    phase_states,
                    child_run_ids,
                    WorkflowRunStatus::Interrupted,
                    None,
                    false,
                )?;
                return Ok(PhaseExecOutcome::Terminated);
            }
            Err(err) => return Err(anyhow!("workflow fan-out failed: {err}")),
        };

        let mut outcomes = Vec::with_capacity(expected_outcomes);
        for item in outcome.outcomes {
            match item.result {
                Ok(value) => outcomes.push(value),
                Err(err) => {
                    return Err(anyhow!(
                        "workflow fan-out: worker {} failed: {err}",
                        item.index
                    ));
                }
            }
        }
        if outcomes.len() != expected_outcomes {
            return Err(anyhow!(
                "workflow fan-out: expected {expected_outcomes} result(s), got {}",
                outcomes.len()
            ));
        }

        // Aggregate worker outcomes in phase order: record every spawned
        // child id, collect completed outputs, and surface the first failure.
        for outcome in outcomes {
            if let Some(oid) = outcome.orchestration_id {
                spawned_this_phase += 1;

View on GitHub (pinned to a221052e0d)

Solutions

  1. Retry via resume_workflow_run — a panicked worker is usually payload- or load-dependent and often passes on retry
  2. Inspect the inner {err} and phase inputs for the failing worker index; simplify that phase's payload
  3. If deterministic, reduce the phase's agent count or concurrency to isolate the panicking worker, then report with logs
Defensive patterns

Strategy: retry

Try / catch

match execute_phase(/* ... */).await {
    Err(e) if e.to_string().contains("worker") && e.to_string().contains("failed") => {
        // executor-level worker failure (panic/join) — resume with backoff; note worker index for triage
        resume_with_backoff(&config, &run_id, 3).await
    }
    other => other,
}

Prevention

When it happens

Trigger: A panic inside the phase-worker closure (prompt building, session clone, response handling) for the agent at position N of the phase, or an abnormal join from the map_reduce executor. Distinct from the child agent failing, which is reported as a phase failure string instead.

Common situations: Engine-side bug triggered by unusual phase payloads (unserializable values, malformed upstream context); executor task abortion under memory pressure; rarely, cancellation racing a worker before the token check.

Related errors


AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16). Data as JSON: /api/errors/d1bf2d497a218dd3. Report an issue: GitHub.