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
- Retry via resume_workflow_run — a panicked worker is usually payload- or load-dependent and often passes on retry
- Inspect the inner {err} and phase inputs for the failing worker index; simplify that phase's payload
- 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
- Remember child-agent failures do NOT raise this error — they arrive as phase output data; an Err here is engine-side
- Record the failing worker index from the message to correlate with the phase's agent list
- Keep phase payloads (upstream context) small and JSON-clean to avoid tripping worker-side panics
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
- workflow fan-out failed: {err}
- workflow fan-out: expected {expected_outcomes} result(s), go
- whatsapp_data write lock poisoned: {e}
- unknown workflow definition: {definition_id}
- unknown workflow run: {id}
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/d1bf2d497a218dd3.
Report an issue: GitHub.