can1357/oh-my-pi · error · ValueError

{context} must be a table

Error message

{context} must be a table

What it means

`_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`.

Source

Thrown at python/robomp/src/persona.py:62

    return _PLACEHOLDER.sub(lambda m: _lookup(m.group(1), scope), template)


@cache
def _load(name: str) -> str:
    return resources.files("robomp.prompts").joinpath(name).read_text(encoding="utf-8")


@cache
def _load_toml(name: str) -> Mapping[str, Any]:
    data = tomllib.loads(_load(name))
    if not isinstance(data, Mapping):
        raise ValueError(f"prompt data file {name!r} must contain a TOML table")
    return data


def _require_mapping(value: Any, context: str) -> Mapping[str, Any]:
    if not isinstance(value, Mapping):
        raise ValueError(f"{context} must be a table")
    return value


def _require_nonempty_str(value: Any, context: str) -> str:
    if not isinstance(value, str) or not value.strip():
        raise ValueError(f"{context} must be a non-empty string")
    return value


def seed_phases(task_kind: str) -> list[dict[str, Any]]:
    raw_phases = _load_toml("todo_phases.toml").get(task_kind, [])
    if not isinstance(raw_phases, list):
        raise ValueError(f"todo_phases.toml[{task_kind!r}] must be a list of phases")

    phases: list[dict[str, Any]] = []
    for phase_index, raw_phase in enumerate(raw_phases):
        phase = _require_mapping(raw_phase, f"todo_phases.toml[{task_kind!r}][{phase_index}]")
        name = _require_nonempty_str(

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the context in the message — it names the exact file, key, and index of the value that is not a table
  2. Ensure the referenced key exists: add the missing `[<tool>]` section in `host_tools.toml`
  3. Convert the offending value into a TOML table: `[create_pr.parameters] issue_number = 'Issue number to reference'`
  4. 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()})"`

Example fix

# before (host_tools.toml)
[create_pr]
description = "Create a PR"
parameters = "no params"

# after
[create_pr]
description = "Create a PR"
[create_pr.parameters]
issue_number = "Issue number to reference"
Defensive patterns

Strategy: type-guard

Validate before calling

import tomllib
tools = tomllib.load(open("host_tools.toml", "rb"))
for name, entry in tools.items():
    assert isinstance(entry, dict), f"{name} entry must be a table"
    assert isinstance(entry.get("parameters", {}), dict), f"{name}.parameters must be a table"

Type guard

def is_mapping(value) -> bool:
    return isinstance(value, Mapping)

Try / catch

try:
    desc = host_tool_parameter_description(tool, param)
except ValueError as e:
    logger.error("host_tools.toml structure invalid: %s", e)
    raise  # config errors should be surfaced, not defaulted

Prevention

When it happens

Trigger: 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.

Common situations: 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`).

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/077f3235c5c4439e. Report an issue: GitHub.