{"record":{"id":"055410a517180a7f","repo":"shareAI-lab/learn-claude-code","slug":"meta-phases-must-be-a-list-of-non-empty-strings","errorCode":null,"errorMessage":"meta.phases must be a list of non-empty strings","messagePattern":"meta\\.phases must be a list of non-empty strings","errorType":"validation","errorClass":"WorkflowInputError","httpStatus":null,"severity":"error","filePath":"s16_workflow_runtime/code.py","lineNumber":136,"sourceCode":"\n# -- Metadata Validation --\ndef validate_meta(meta):\n    \"\"\"Validate name, description, and optional phases before launch.\"\"\"\n    if not isinstance(meta, dict):\n        raise WorkflowInputError(\"meta must be an object literal\")\n    if not meta.get(\"name\") or not meta.get(\"description\"):\n        raise WorkflowInputError(\"meta requires `name` and `description`\")\n    if not isinstance(meta[\"name\"], str) or not WORKFLOW_NAME_RE.fullmatch(meta[\"name\"]):\n        raise WorkflowInputError(\n            \"meta.name must be a 1-64 character slug using letters, numbers, '.', '_', or '-'\"\n        )\n    if not isinstance(meta[\"description\"], str):\n        raise WorkflowInputError(\"meta.description must be a string\")\n    if \"phases\" in meta:\n        if not isinstance(meta[\"phases\"], list) or not all(\n            isinstance(phase, str) and phase for phase in meta[\"phases\"]\n        ):\n            raise WorkflowInputError(\"meta.phases must be a list of non-empty strings\")\n    return meta\n\n\ndef check_permission(meta, settings=None):\n    \"\"\"Apply the s03 allow/deny gate before launching a workflow.\"\"\"\n    settings = settings or {}\n    if meta[\"name\"] in settings.get(\"deny\", []):\n        raise WorkflowInputError(f\"workflow '{meta['name']}' denied by settings\")\n    return \"allow\"\n\n\n# -- Minimal JSON Schema --\nclass SimpleJsonSchema:\n    \"\"\"Tiny validator backing agent({schema}):\n    object/array/string/boolean/number + required keys.\"\"\"\n\n    def __init__(self, schema):\n        self.schema = schema","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s16_workflow_runtime/code.py#L118-L154","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Always use a list of non-empty strings: [\"extract\", \"transform\", \"load\"]","In YAML, write single phases as 'phases: [extract]' so they parse as lists","Filter out falsy entries when generating phases programmatically: [p for p in raw if isinstance(p, str) and p]"],"exampleFix":"# before\nmeta = {\"name\": \"etl\", \"description\": \"...\", \"phases\": \"extract,transform\"}\n\n# after\nmeta = {\"name\": \"etl\", \"description\": \"...\", \"phases\": [\"extract\", \"transform\"]}","handlingStrategy":"validation","validationCode":"def phases_are_valid(meta: dict) -> bool:\n    phases = meta.get(\"phases\")\n    return phases is None or (\n        isinstance(phases, list)\n        and all(isinstance(p, str) and p for p in phases)\n    )\n\nassert phases_are_valid(meta)","typeGuard":"def meta_phases_ok(meta) -> bool:\n    if \"phases\" not in meta:\n        return True\n    phases = meta[\"phases\"]\n    return isinstance(phases, list) and all(isinstance(p, str) and p for p in phases)","tryCatchPattern":"try:\n    validate_meta(meta)\nexcept WorkflowInputError as exc:\n    if \"meta.phases\" in str(exc):\n        raw = meta.get(\"phases\")\n        phases = [p.strip() for p in raw.split(\",\")] if isinstance(raw, str) else [p for p in raw if isinstance(p, str) and p]\n        meta = {**meta, \"phases\": phases}\n        validate_meta(meta)\n    else:\n        raise","preventionTips":["Write single phases as [\"name\"] in YAML, not a scalar","Filter empties when generating phase lists","Omit the phases key entirely when unused"],"tags":["workflow","validation","metadata","phases","type-error"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}