can1357/oh-my-pi · error · ValueError

todo_phases.toml[{task_kind!r}] must be a list of phases

Error message

todo_phases.toml[{task_kind!r}] must be a list of phases

What it means

`seed_phases(task_kind)` loads `todo_phases.toml`, reads the `task_kind` key, and requires its value to be a list of phase tables. When the value under that key exists but is not a list (string, integer, or bare table), it raises ValueError with the offending key quoted. A missing key returns [] without error — only a present-but-wrong-typed value triggers this message.

Source

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

    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(
            phase.get("name"),
            f"todo_phases.toml[{task_kind!r}][{phase_index}].name",
        )
        raw_tasks = phase.get("tasks")
        if not isinstance(raw_tasks, list) or not raw_tasks:
            raise ValueError(f"todo_phases.toml[{task_kind!r}][{phase_index}].tasks must be a non-empty list")
        tasks = [
            _require_nonempty_str(
                task,
                f"todo_phases.toml[{task_kind!r}][{phase_index}].tasks[{task_index}]",
            )
            for task_index, task in enumerate(raw_tasks)
        ]

View on GitHub (pinned to 9690622007)

Solutions

  1. Confirm the value type: `python -c "import tomllib; print(type(tomllib.load(open('todo_phases.toml','rb'))['bug']))"` must be list
  2. Rewrite the entry as an array of tables: `bug = [{ name = "reproduce", tasks = ["...", "..."] }, ...]` or `[[todo_phases.bug]]` blocks
  3. Restore the original file if a merge broke it: `git checkout -- python/robomp/src/prompts/todo_phases.toml`
  4. Restart the process after fixing — `_load_toml` results are `@cache`d for the process lifetime

Example fix

# before (todo_phases.toml)
[todo_phases]
bug = "reproduce then fix"

# after
[todo_phases]
bug = [{ name = "reproduce", tasks = ["write failing repro", "confirm failure"] }, { name = "fix", tasks = ["patch", "run tests"] }]
Defensive patterns

Strategy: validation

Validate before calling

import tomllib
phases = tomllib.load(open("todo_phases.toml", "rb")).get("bug")
if phases is not None:
    assert isinstance(phases, list), "todo_phases[bug] must be a list"
    for i, ph in enumerate(phases):
        assert isinstance(ph, dict) and ph.get("name") and ph.get("tasks"), f"phase {i} malformed"

Type guard

def is_phase_list(value) -> bool:
    return isinstance(value, list) and all(isinstance(p, dict) for p in value)

Try / catch

try:
    phases = seed_phases(task_kind)
except ValueError as e:
    logger.error("todo_phases.toml malformed for %s: %s", task_kind, e)
    raise  # or degrade to [] if the workflow tolerates no seeded phases

Prevention

When it happens

Trigger: Calling `seed_phases('bug')`, `seed_phases('question')`, etc., when `todo_phases.toml` defines `bug = "reproduce then fix"`, `bug = 3`, or `[todo_phases.bug]` (a bare table) instead of an array of phase tables such as `bug = [{ name = ..., tasks = [...] }]` or `[[todo_phases.bug]]` blocks.

Common situations: A hand-edit or bad merge replacing the array with a scalar; a YAML/JSON-style paste where list brackets were lost; using `[todo_phases.bug]` table syntax (raises) instead of `[[todo_phases.bug]]` array-of-tables; seeing this error means the key exists — a typo'd task_kind would silently return [] instead.

Related errors


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