shareAI-lab/learn-claude-code · error · WorkflowInputError
unknown workflow '{name}'
Error message
unknown workflow '{name}' What it means
ExecutionState.workflow() looks the requested name up in the module-level WORKFLOWS registry of saved workflows; an unknown name raises WorkflowInputError("unknown workflow '{name}'"). Only names explicitly registered in WORKFLOWS (host-trusted code) can run — this is the trust boundary the docstring 'resolve trusted code from the host registry' refers to.
Source
Thrown at s16_workflow_runtime/code.py:527
async def pipeline(self, items, *stages):
"""Per-item staged flow, NO barrier between stages: item A can be in
stage 3 while item B is still in stage 1. Each stage gets
(prev_result, original_item, index). A throwing stage fails the workflow."""
async def run_item(item, idx):
value = item
for stage in stages:
value = await stage(value, item, idx)
return value
return await asyncio.gather(*[run_item(it, i) for i, it in enumerate(items)])
async def workflow(self, name, args=None):
"""Run a saved workflow inline as a child (one level), sharing this run's
journal + budget + agent counter."""
if self._depth >= 1:
raise WorkflowInputError("workflow() nesting is one level only")
if name not in WORKFLOWS:
raise WorkflowInputError(f"unknown workflow '{name}'")
meta, fn = WORKFLOWS[name]
child = ExecutionState(self.task, self.journal, self.runner, self.budget,
args or {}, depth=self._depth + 1,
limits=self._limits)
return await fn(child, args or {})
# -- Workflow Tool --
class WorkflowTool:
"""The Workflow tool. .call() validates meta, runs the permission check,
creates runId/taskId, registers a LocalWorkflowTask, and emits lifecycle
events while executing the script. It returns the result and task state and
supports resume."""
async def call(self, meta, script_fn, args=None, resume_from_run_id=None):
validate_meta(meta)
check_permission(meta)
resuming = resume_from_run_id is not NoneView on GitHub (pinned to 985456f4ad)
Solutions
- Check WORKFLOWS.keys() (or the tool's workflow listing) and use the exact registered name.
- If the workflow should exist, register it in WORKFLOWS with its meta and script function before calling.
- Fix casing/typo: names are matched exactly and must satisfy the workflow-name pattern.
Example fix
# before
await ctx.workflow("code-review") # registry has "review"
# after
await ctx.workflow("review") Defensive patterns
Strategy: type-guard
Validate before calling
from s16_workflow_runtime import WORKFLOWS
if name not in WORKFLOWS:
raise KeyError(f"available: {sorted(WORKFLOWS)}")
await ctx.workflow(name, args) Type guard
def is_registered_workflow(name) -> bool:
return isinstance(name, str) and name in WORKFLOWS Try / catch
try:
await ctx.workflow(name, args)
except WorkflowInputError as e:
if "unknown workflow" in str(e):
pick = match_closest(name, WORKFLOWS.keys()) # suggest nearest
raise ValueError(f"did you mean {pick}?") from e
raise Prevention
- Expose the registry listing to callers so names are discovered, not guessed.
- Centralize workflow names as constants shared between registrar and call sites.
- Validate names against WORKFLOWS before launching long-running runs.
When it happens
Trigger: Calling ctx.workflow(name) from inside a running workflow where name is not a key in WORKFLOWS — typo, unregistered workflow, or a name that exists only in a different deployment.
Common situations: Renaming a workflow without updating call sites; expecting dynamically defined workflows to be runnable; running against a registry built from an older version of the module.
Related errors
- invalid workflow runId
- meta must be an object literal
- meta requires `name` and `description`
- meta.name must be a 1-64 character slug using letters, numbe
- meta.description must be a string
AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14).
Data as JSON: /api/errors/de1516241c68dc7f.
Report an issue: GitHub.