{"record":{"id":"bcab26f507a73ad0","repo":"bmad-code-org/BMAD-METHOD","slug":"item-label-must-be-a-table","errorCode":null,"errorMessage":"{item_label} must be a table","messagePattern":"(.+?) must be a table","errorType":"validation","errorClass":"RenderError","httpStatus":null,"severity":"error","filePath":"src/scripts/render_skill.py","lineNumber":80,"sourceCode":"\ndef _require_string_list(value: Any, label: str) -> list[str]:\n    if not isinstance(value, list):\n        raise RenderError(f\"{label} must be a list, got {type(value).__name__}\")\n    result = []\n    for index, item in enumerate(value):\n        result.append(_require_string(item, f\"{label}[{index}]\"))\n    return result\n\n\ndef _require_review_layers(value: Any, label: str) -> list[dict[str, str]]:\n    if not isinstance(value, list):\n        raise RenderError(f\"{label} must be a list of tables\")\n    result: list[dict[str, str]] = []\n    seen: set[str] = set()\n    for index, item in enumerate(value):\n        item_label = f\"{label}[{index}]\"\n        if not isinstance(item, dict):\n            raise RenderError(f\"{item_label} must be a table\")\n        identifier = _require_string(item.get(\"id\"), f\"{item_label}.id\")\n        if identifier in seen:\n            raise RenderError(f\"duplicate review layer id `{identifier}`\")\n        seen.add(identifier)\n        layer = {\n            \"id\": identifier,\n            \"name\": _require_string(item.get(\"name\", identifier), f\"{item_label}.name\"),\n            \"instruction\": _require_string(\n                item.get(\"instruction\"), f\"{item_label}.instruction\", allow_empty=True\n            ),\n        }\n        if \"when\" in item:\n            layer[\"when\"] = _require_string(item[\"when\"], f\"{item_label}.when\")\n        result.append(layer)\n    return result\n\n\ndef _load_sources(skill_dir: Path) -> dict[str, str]:","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/bmad-code-org/BMAD-METHOD/blob/b70486b9bdcb0a404d329e2a763b57964e7f1360/src/scripts/render_skill.py#L62-L98","documentation":"Inside _require_review_layers, each element of the review-layers array must be a TOML table. This fires when one element is a scalar, string, or nested array. The {item_label} pinpoints the offending index, e.g. customization.workflow.review_layers[2].","triggerScenarios":"The merged review-layers array contains a non-dict element -- e.g. review_layers = [\"architect\", {id=\"pm\"}] mixing strings and tables, or a malformed [[...]] block. isinstance(item, dict) is False for that index.","commonSituations":"Hand-editing TOML and forgetting the [[...]] header for one layer; copy-paste leaving a stray string; a non-keyed merge appending a scalar into the array.","solutions":["Open the file at the index reported by {item_label} and make that entry a table (its own [[workflow.review_layers]] block or an inline table).","Remove any scalar/stray entries from the array.","Re-parse with tomllib and assert every element is a dict containing at least an id key."],"exampleFix":"# before\n[[workflow.review_layers]]\nid = \"architect\"\n\nworkflow.review_layers = [\"pm\"]   # string, not a table\n\n# after\n[[workflow.review_layers]]\nid = \"architect\"\n\n[[workflow.review_layers]]\nid = \"pm\"","handlingStrategy":"validation","validationCode":"import tomllib\nfrom pathlib import Path\n\ndef validate_all_layers_are_tables(toml_path: Path, dotted: str) -> None:\n    data = tomllib.loads(Path(toml_path).read_text(encoding=\"utf-8\"))\n    cur: object = data\n    for part in dotted.split(\".\"):\n        cur = cur[part]\n    assert isinstance(cur, list), f\"{dotted} is not a list\"\n    for i, item in enumerate(cur):\n        if not isinstance(item, dict):\n            raise SystemExit(f\"{dotted}[{i}] must be a table, got {type(item).__name__}\")","typeGuard":"def all_tables(value: list) -> bool:\n    return all(isinstance(x, dict) for x in value)","tryCatchPattern":null,"preventionTips":["Give every review layer its own [[workflow.review_layers]] block.","After editing TOML, re-parse and check each element is a dict.","Add a pre-render lint that rejects mixed-type arrays in customization fields."],"tags":["python","toml","customization","validation","review-layers"],"backgroundTag":null,"analyzedSha":"b70486b9bdcb0a404d329e2a763b57964e7f1360","analyzedAt":"2026-08-13T01:21:12.247Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}