zeroclaw-labs/zeroclaw · error · anyhow::Error
completed run has no step results to distill
Error message
completed run has no step results to distill
What it means
A defensive guard in capture_successful_run: the run reached Completed but its step_results slice is empty, so there is nothing to distill into a procedure. Completed runs are expected to carry at least one step result; an empty set means the SOP effectively executed zero recorded steps (or the run record is degenerate).
Source
Thrown at crates/zeroclaw-runtime/src/sop/procedural_memory.rs:97
rollback_path: None,
};
engine.save_proposal(&proposal)?;
Ok(proposal)
}
pub fn capture_successful_run(
engine: &SopEngine,
run_id: &str,
requested_by: Option<String>,
) -> Result<ProposalRecord> {
let run = engine
.get_run(run_id)
.ok_or_else(|| anyhow::Error::msg(format!("SOP run not found: {run_id}")))?;
if run.status != SopRunStatus::Completed {
bail!("only completed SOP runs can be captured");
}
if run.step_results.is_empty() {
bail!("completed run has no step results to distill");
}
if run
.step_results
.iter()
.any(|step| matches!(step.status, super::types::SopStepStatus::Failed))
{
bail!("failed step output is not captured into procedural memory");
}
let sop = engine
.get_sop(&run.sop_name)
.ok_or_else(|| anyhow::Error::msg(format!("SOP not loaded: {}", run.sop_name)))?;
let manifest_toml = read_or_default_manifest(sop)?;
let procedure_markdown = append_captured_notes(sop, run_id, &run.step_results)?;
create_proposal(
engine,
ProposalDraft {
sop_name: sop.name.clone(),View on GitHub (pinned to 88bb9c8533)
Solutions
- Verify the SOP actually has steps before running and capturing (validate authoring with validate_sop_strict).
- Check engine.get_run(run_id).step_results before capture and skip degenerate runs.
- If a real run shows Completed with no results, investigate the engine's step recording for that run.
Example fix
// before: capture any completed run
let p = capture_successful_run(&engine, run_id, None).await?;
// after: require at least one recorded step
let run = engine.get_run(run_id).context("run missing")?;
if run.status == SopRunStatus::Completed && !run.step_results.is_empty() {
let p = capture_successful_run(&engine, run_id, None).await?;
} Defensive patterns
Strategy: validation
Validate before calling
let Some(run) = engine.get_run(run_id) else {
anyhow::bail!("SOP run not found: {run_id}");
};
anyhow::ensure!(
run.status == SopRunStatus::Completed && !run.step_results.is_empty(),
"run has nothing to distill"
); Type guard
fn has_distillable_steps(run: &SopRun) -> bool {
!run.step_results.is_empty()
} Try / catch
match capture_successful_run(&engine, run_id, None).await {
Err(e) if e.to_string().contains("no step results to distill") => {
// degenerate run: verify the SOP has steps, then re-run it
}
other => other?,
} Prevention
- Reject zero-step SOPs at authoring time.
- Pre-check step_results before capture.
- Investigate any Completed run with no recorded results as an engine anomaly.
When it happens
Trigger: Capturing a run of a zero-step or placeholder SOP that 'completes' without executing any steps; a run record reconstructed or trimmed in a way that dropped step results.
Common situations: Authoring scaffolding SOPs with no steps and running them as a smoke test; test fixtures that mark a run Completed manually without step results.
Related errors
- only completed SOP runs can be captured
- failed step output is not captured into procedural memory
- proposal rejected: {reason}
- proposal {} is {:?}, not pending
- proposal {} is stale; target SOP now exists
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/e1524104633175d7.
Report an issue: GitHub.