shareAI-lab/learn-claude-code · error · WorkflowInputError

meta.phases must be a list of non-empty strings

Error message

meta.phases must be a list of non-empty strings

What it means

When meta includes 'phases', it must be a list whose every element is a non-empty string. Anything else — a string, a dict, None, or a list containing empties/non-strings — raises WorkflowInputError. Phases are optional; the check only runs when the key is present.

Source

Thrown at s16_workflow_runtime/code.py:136

# -- Metadata Validation --
def validate_meta(meta):
    """Validate name, description, and optional phases before launch."""
    if not isinstance(meta, dict):
        raise WorkflowInputError("meta must be an object literal")
    if not meta.get("name") or not meta.get("description"):
        raise WorkflowInputError("meta requires `name` and `description`")
    if not isinstance(meta["name"], str) or not WORKFLOW_NAME_RE.fullmatch(meta["name"]):
        raise WorkflowInputError(
            "meta.name must be a 1-64 character slug using letters, numbers, '.', '_', or '-'"
        )
    if not isinstance(meta["description"], str):
        raise WorkflowInputError("meta.description must be a string")
    if "phases" in meta:
        if not isinstance(meta["phases"], list) or not all(
            isinstance(phase, str) and phase for phase in meta["phases"]
        ):
            raise WorkflowInputError("meta.phases must be a list of non-empty strings")
    return meta


def check_permission(meta, settings=None):
    """Apply the s03 allow/deny gate before launching a workflow."""
    settings = settings or {}
    if meta["name"] in settings.get("deny", []):
        raise WorkflowInputError(f"workflow '{meta['name']}' denied by settings")
    return "allow"


# -- Minimal JSON Schema --
class SimpleJsonSchema:
    """Tiny validator backing agent({schema}):
    object/array/string/boolean/number + required keys."""

    def __init__(self, schema):
        self.schema = schema

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Always use a list of non-empty strings: ["extract", "transform", "load"]
  2. In YAML, write single phases as 'phases: [extract]' so they parse as lists
  3. Filter out falsy entries when generating phases programmatically: [p for p in raw if isinstance(p, str) and p]

Example fix

# before
meta = {"name": "etl", "description": "...", "phases": "extract,transform"}

# after
meta = {"name": "etl", "description": "...", "phases": ["extract", "transform"]}
Defensive patterns

Strategy: validation

Validate before calling

def phases_are_valid(meta: dict) -> bool:
    phases = meta.get("phases")
    return phases is None or (
        isinstance(phases, list)
        and all(isinstance(p, str) and p for p in phases)
    )

assert phases_are_valid(meta)

Type guard

def meta_phases_ok(meta) -> bool:
    if "phases" not in meta:
        return True
    phases = meta["phases"]
    return isinstance(phases, list) and all(isinstance(p, str) and p for p in phases)

Try / catch

try:
    validate_meta(meta)
except WorkflowInputError as exc:
    if "meta.phases" in str(exc):
        raw = meta.get("phases")
        phases = [p.strip() for p in raw.split(",")] if isinstance(raw, str) else [p for p in raw if isinstance(p, str) and p]
        meta = {**meta, "phases": phases}
        validate_meta(meta)
    else:
        raise

Prevention

When it happens

Trigger: meta = {"phases": "extract, transform"} (a single string instead of a list). meta = {"phases": ["extract", ""]} (trailing comma produced an empty element). meta = {"phases": [{"name": "extract"}]} (objects instead of strings).

Common situations: Config formats where a one-element list is written as a scalar (YAML 'phases: extract'). Programmatic phase lists with filtered/empty results. Refactors from structured phase objects to plain names that stop halfway.

Related errors


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