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

workflow source already registered: {run_id}

Error message

workflow source already registered: {run_id}

What it means

WorkflowStore::register validates the run_id and then rejects registration if a source for that run_id is already present in its in-memory sources map, returning io::ErrorKind::AlreadyExists with the run_id. Each workflow run must have exactly one registered resume source (script/args/effort).

Source

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

                    state.token_leases.clear();
                    state.agent_usage_incomplete = true;
                }
                states.push(state);
            }
        }
        (store, states)
    }

    pub(crate) fn register(
        &self,
        run_id: &str,
        script: &str,
        args: &serde_json::Value,
        effort: Option<ReasoningEffort>,
    ) -> io::Result<()> {
        validate_run_id(run_id)?;
        if self.sources.lock().contains_key(run_id) {
            return Err(io::Error::new(
                io::ErrorKind::AlreadyExists,
                format!("workflow source already registered: {run_id}"),
            ));
        }

        if let Some(run_dir) = self.run_dir(run_id) {
            let scripts_dir = run_dir.join("scripts");
            std::fs::create_dir_all(&scripts_dir)?;
            let args_json = serde_json::to_vec_pretty(args).map_err(io::Error::other)?;
            atomic_write_new(&run_dir.join("args.json"), &args_json)?;
            if let Some(effort) = effort {
                atomic_write_new(&run_dir.join("effort"), effort.as_str().as_bytes())?;
            }
            atomic_write_new(&script_revision_path(&run_dir, 0), script.as_bytes())?;
            atomic_write_replace(&run_dir.join("script.rhai"), script.as_bytes())?;
        }

        self.sources.lock().insert(

View on GitHub (pinned to bc7f02eddd)

Solutions

  1. Check store has source / contains-key for the run_id first and skip re-registration
  2. Treat AlreadyExists as idempotent success if script and args match the existing registration
  3. Use a fresh run_id for genuinely new runs (e.g. UUIDv7 per run)
  4. If re-registration is legitimate, add an unregister/replace API instead of calling register again

Example fix

// before
store.register(&run_id, &script, &args, effort)?; // panics/fails on resume
// after
if store.source(&run_id).is_none() {
    store.register(&run_id, &script, &args, effort)?;
}
Defensive patterns

Strategy: validation

Validate before calling

if store.source(&run_id).is_some() {
    // already registered: skip or verify script/args match
} else {
    store.register(&run_id, &script, &args, effort)?;
}

Type guard

fn is_registered(store: &WorkflowStore, run_id: &str) -> bool {
    store.source(run_id).is_some()
}

Try / catch

match store.register(&run_id, &script, &args, effort) {
    Err(e) if e.kind() == io::ErrorKind::AlreadyExists => { /* idempotent: verify and continue */ }
    other => other?,
}

Prevention

When it happens

Trigger: Calling register twice with the same run_id — e.g. re-registering on resume, a retried registration, or two components both registering the same run.

Common situations: Retry logic that re-invokes register after a transient failure that actually succeeded; resuming a run whose source was already registered in this process; duplicate event handling registering the same run_id twice.

Related errors


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