Hmbown/CodeWhale · error · Error
${name} is unavailable in Workflow scripts: runs must be det
Error message
${name} is unavailable in Workflow scripts: runs must be deterministic for record/replay What it means
Thrown by NativeMemoryStore::scope_path when a memory operation is scoped to MemoryScope::Workspace but workspace_id is None. Workspace memory is stored per-repository under root/workspace/<id>/MEMORY.md, so an identifier is mandatory to pick the directory. The id is normally derived by hashing the git remote.origin.url (NativeMemoryStore::workspace_id), which returns None for non-git directories or repos without an origin remote.
Source
Thrown at crates/workflow-js/src/vm.rs:1033
"explore" | "explorer" | "scout" | "plan" | "planner" | "review" | "reviewer"
| "verify" | "verifier" => Some(TaskRoleKind::ReadOnly),
"general" | "worker" => Some(TaskRoleKind::General),
"implement" | "implementer" | "builder" => Some(TaskRoleKind::Implementer),
_ => None,
}
}
/// The JS prelude injected before every script: determinism bans, the
/// `task`/`parallel`/`pipeline`/`log`/`phase` stdlib (design §7), and the
/// `budget` global.
fn prelude() -> String {
PRELUDE_TEMPLATE.replace("__MAX_ITEMS__", &PARALLEL_MAX_ITEMS.to_string())
}
const PRELUDE_TEMPLATE: &str = r#""use strict";
(() => {
const banned = (name) => () => {
throw new Error(name + " is unavailable in Workflow scripts: runs must be deterministic for record/replay");
};
const BannedDate = function Date() {
throw new Error("new Date()/Date() is unavailable in Workflow scripts: runs must be deterministic for record/replay");
};
BannedDate.now = banned("Date.now()");
BannedDate.parse = banned("Date.parse()");
BannedDate.UTC = banned("Date.UTC()");
globalThis.Date = BannedDate;
Math.random = banned("Math.random()");
// Capture temporary host bindings into this closure, then strip them from
// globalThis so scripts only see the documented Workflow surface (#4129).
const hostTask = __workflow_task;
const hostLog = __workflow_log;
const hostPhase = __workflow_phase;
const hostBudgetTotal = __workflow_budget_total;
const hostBudgetSpent = __workflow_budget_spent;
const hostBudgetRemaining = __workflow_budget_remaining;View on GitHub (pinned to 8880682c63)
Solutions
- Derive the id first and bail out early: let id = NativeMemoryStore::workspace_id(cwd)?; if None, fall back to MemoryScope::Global or surface 'workspace memory requires a git origin remote' to the user.
- If you are in a repo, add an origin remote: git remote add origin <url>, then retry.
- If workspace isolation is not needed, call the same API with MemoryScope::Global, which needs no id.
- In tests, pass a fixed id (e.g. Some("repo-a")) like native_memory/tests.rs does.
Example fix
// before
store.remember(MemoryScope::Workspace, None, "note")?;
// after
let id = NativeMemoryStore::workspace_id(&cwd)?
.context("workspace memory requires a git origin remote")?;
store.remember(MemoryScope::Workspace, Some(&id), "note")?; Defensive patterns
Strategy: validation
Validate before calling
let workspace_id = NativeMemoryStore::workspace_id(&cwd)?;
let scope = match (&workspace_id, wanted_scope) {
(Some(_), MemoryScope::Workspace) => MemoryScope::Workspace,
(None, MemoryScope::Workspace) => MemoryScope::Global, // or bail with your own message
(_, other) => other,
};
store.remember(scope, workspace_id.as_deref(), note)?; Type guard
fn workspace_scope_available(cwd: &Path) -> bool {
Command::new("git").arg("-C").arg(cwd)
.args(["config", "--get", "remote.origin.url"])
.output().map(|o| o.status.success() && !o.stdout.trim().is_empty()).unwrap_or(false)
} Try / catch
match store.remember(MemoryScope::Workspace, workspace_id.as_deref(), note) {
Ok(_) => {}
Err(e) if e.to_string().contains("workspace scope requires a workspace id") => {
// degrade to global scope or tell the user to add an origin remote
}
Err(e) => return Err(e),
} Prevention
- Always derive workspace_id via NativeMemoryStore::workspace_id before any Workspace-scoped call.
- Treat a None id as 'workspace memory unavailable' and fall back to Global or inform the user.
- Ensure repos used with workspace memory have `git remote add origin <url>` set.
- In tests, pass a deterministic Some("repo-x") id like the native memory test-suite does.
When it happens
Trigger: Calling remember/edit/retire with MemoryScope::Workspace and workspace_id=None; e.g. store.remember(MemoryScope::Workspace, None, "note"). The tool layer (tools/native_memory.rs, tools/remember.rs) hits this when the session's workspace has no derivable git origin, so no id is passed down.
Common situations: Running the TUI in a plain folder that is not a git repository; a freshly created repo that has no `origin` remote yet; a detached worktree cloned without remotes; tests that construct MemoryScope::Workspace directly without first computing an id.
Related errors
- new Date()/Date() is unavailable in Workflow scripts: runs m
- task(): expected an options object
- ${envelope.error}
- pipeline(): expected an array of items
- Thread summaries require the runtime bearer token.
AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16).
Data as JSON: /api/errors/71bab292858eda19.
Report an issue: GitHub.