xai-org/grok-build · error · std::io::Error

workflow state has no registered resume source

Error message

workflow state has no registered resume source

What it means

persist_now writes workflow state synchronously but first requires a registered resume source via manifest_for; if the WorkflowRunState's run_id was never registered (or the store was reloaded without re-registration), it returns io::ErrorKind::NotFound "workflow state has no registered resume source". State cannot be persisted without knowing how to resume the run.

Source

Thrown at crates/codegen/xai-grok-shell/src/session/workflow/store.rs:178

        Ok(())
    }

    fn manifest_for(&self, state: &WorkflowRunState) -> Option<WorkflowRunManifest> {
        let revision = self
            .sources
            .lock()
            .get(&state.run_id)
            .map(|source| source.revision)?;
        Some(WorkflowRunManifest {
            version: WORKFLOW_RUN_MANIFEST_VERSION,
            state: state.clone(),
            script_revision: revision,
        })
    }

    pub(crate) fn persist_now(&self, state: &WorkflowRunState) -> io::Result<()> {
        let Some(manifest) = self.manifest_for(state) else {
            return Err(io::Error::new(
                io::ErrorKind::NotFound,
                "workflow state has no registered resume source",
            ));
        };
        let Some(run_dir) = self.run_dir(&state.run_id) else {
            return Ok(());
        };
        let json = serde_json::to_vec_pretty(&manifest).map_err(io::Error::other)?;
        atomic_write_replace(&run_dir.join("state.json"), &json)
    }

    pub(crate) fn persist(&self, state: &WorkflowRunState) -> io::Result<()> {
        let manifest = self.manifest_for(state).ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::NotFound,
                "workflow state has no registered resume source",
            )
        })?;

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Call register(run_id, script, args, effort) before persist_now
  2. Ensure the resume/restore path re-registers sources before any persistence
  3. Verify the run_id in the state matches the one registered (no id mismatch)
  4. If persistence is optional for this run, skip persist_now instead of propagating the error

Example fix

// before
store.persist_now(&state)?; // NotFound after reload
// after
store.register(&state.run_id, &script, &args, effort)?;
store.persist_now(&state)?;
Defensive patterns

Strategy: validation

Validate before calling

if store.source(&state.run_id).is_none() {
    store.register(&state.run_id, &script, &args, effort)?;
}
store.persist_now(&state)?;

Type guard

fn can_persist(store: &WorkflowStore, state: &WorkflowRunState) -> bool {
    store.source(&state.run_id).is_some()
}

Try / catch

match store.persist_now(&state) {
    Err(e) if e.kind() == io::ErrorKind::NotFound
        && e.to_string().contains("resume source") => {
        store.register(&state.run_id, &script, &args, effort)?;
        store.persist_now(&state)?;
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling persist_now with a WorkflowRunState whose run_id has no entry in the store's sources map — e.g. after process restart/reload without re-registering, or persisting a state constructed for an unregistered run.

Common situations: Restoring state from disk after a crash but resuming before re-registering the script/args; a bug where register was skipped on a fast-fail path; persisting a stale state object from a previous session.

Related errors


AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31). Data as JSON: /api/errors/87454aa53f6dcb6a. Report an issue: GitHub.