{"record":{"id":"077f3235c5c4439e","repo":"can1357/oh-my-pi","slug":"context-must-be-a-table","errorCode":null,"errorMessage":"{context} must be a table","messagePattern":"(.+?) must be a table","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/robomp/src/persona.py","lineNumber":62,"sourceCode":"    return _PLACEHOLDER.sub(lambda m: _lookup(m.group(1), scope), template)\n\n\n@cache\ndef _load(name: str) -> str:\n    return resources.files(\"robomp.prompts\").joinpath(name).read_text(encoding=\"utf-8\")\n\n\n@cache\ndef _load_toml(name: str) -> Mapping[str, Any]:\n    data = tomllib.loads(_load(name))\n    if not isinstance(data, Mapping):\n        raise ValueError(f\"prompt data file {name!r} must contain a TOML table\")\n    return data\n\n\ndef _require_mapping(value: Any, context: str) -> Mapping[str, Any]:\n    if not isinstance(value, Mapping):\n        raise ValueError(f\"{context} must be a table\")\n    return value\n\n\ndef _require_nonempty_str(value: Any, context: str) -> str:\n    if not isinstance(value, str) or not value.strip():\n        raise ValueError(f\"{context} must be a non-empty string\")\n    return value\n\n\ndef seed_phases(task_kind: str) -> list[dict[str, Any]]:\n    raw_phases = _load_toml(\"todo_phases.toml\").get(task_kind, [])\n    if not isinstance(raw_phases, list):\n        raise ValueError(f\"todo_phases.toml[{task_kind!r}] must be a list of phases\")\n\n    phases: list[dict[str, Any]] = []\n    for phase_index, raw_phase in enumerate(raw_phases):\n        phase = _require_mapping(raw_phase, f\"todo_phases.toml[{task_kind!r}][{phase_index}]\")\n        name = _require_nonempty_str(","sourceCodeStart":44,"sourceCodeEnd":80,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/persona.py#L44-L80","documentation":"`_require_mapping(value, context)` validates that a value loaded from a prompt TOML data file is a table (Mapping) and returns it; otherwise it raises ValueError with a context string naming the exact file, key, and index where the bad value lives. It guards nested structures such as each phase in `todo_phases.toml[<kind>][i]` and `host_tools.toml[<tool>].parameters`.","triggerScenarios":"Calling `seed_phases(task_kind)` where a phase entry is a scalar/string instead of a `[phase]` table; `_host_tool_entry(tool_name)` (via `host_tool_description`/`host_tool_parameter_description`) when the tool key is missing from `host_tools.toml` (`.get()` returns None) or defined as a non-table; `classify_next_step` reading a malformed tool/parameter entry.","commonSituations":"Misspelled tool name in `host_tools.toml` so the key lookup returns None; writing `parameters = \"none\"` as a string instead of a `[<tool>.parameters]` table; a phase in `todo_phases.toml` written as a bare string inside the list; a TOML typo turning a section into a value (`parameters = true`).","solutions":["Read the context in the message — it names the exact file, key, and index of the value that is not a table","Ensure the referenced key exists: add the missing `[<tool>]` section in `host_tools.toml`","Convert the offending value into a TOML table: `[create_pr.parameters] issue_number = 'Issue number to reference'`","Audit the whole file: `python -c \"import tomllib; d=tomllib.load(open('host_tools.toml','rb')); print({k: type(v).__name__ for k,v in d.items()})\"`"],"exampleFix":"# before (host_tools.toml)\n[create_pr]\ndescription = \"Create a PR\"\nparameters = \"no params\"\n\n# after\n[create_pr]\ndescription = \"Create a PR\"\n[create_pr.parameters]\nissue_number = \"Issue number to reference\"","handlingStrategy":"type-guard","validationCode":"import tomllib\ntools = tomllib.load(open(\"host_tools.toml\", \"rb\"))\nfor name, entry in tools.items():\n    assert isinstance(entry, dict), f\"{name} entry must be a table\"\n    assert isinstance(entry.get(\"parameters\", {}), dict), f\"{name}.parameters must be a table\"","typeGuard":"def is_mapping(value) -> bool:\n    return isinstance(value, Mapping)","tryCatchPattern":"try:\n    desc = host_tool_parameter_description(tool, param)\nexcept ValueError as e:\n    logger.error(\"host_tools.toml structure invalid: %s\", e)\n    raise  # config errors should be surfaced, not defaulted","preventionTips":["Keep every tool entry as `[tool_name]` with a `[tool_name.parameters]` sub-table","Validate host_tools.toml at startup with a schema check over all entries","Wrap any non-table value with a table header rather than using a string/inline value","Keep TOML and Python tool definitions in sync — test that every registered tool has a TOML entry"],"tags":["toml","configuration","type-guard","validation"],"backgroundTag":"toml-schema-validation-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}