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 None

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Check WORKFLOWS.keys() (or the tool's workflow listing) and use the exact registered name.
  2. If the workflow should exist, register it in WORKFLOWS with its meta and script function before calling.
  3. 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

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


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/de1516241c68dc7f. Report an issue: GitHub.