bmad-code-org/BMAD-METHOD · error · ValueError

each --extra entry must be a JSON object, got: {item!r}

Error message

each --extra entry must be a JSON object, got: {item!r}

What it means

After confirming the `--extra` overlay is a list, pick_methods.py iterates each element and requires every one to be a JSON object. This error names the offending element via `{item!r}`. It guards the next line, which calls `item.get(k)` for each field in FIELDS — only dicts have `.get`. A scalar or null element would otherwise raise an `AttributeError` deep in the merge.

Source

Thrown at src/core-skills/bmad-advanced-elicitation/scripts/pick_methods.py:65

    with open(file, newline="", encoding="utf-8-sig") as f:
        rows = list(csv.DictReader(f))
    for r in rows:
        for k in FIELDS:
            r.setdefault(k, "")
            r[k] = (r.get(k) or "").strip()
    return rows


def load_extra(spec: str) -> list[dict]:
    """Parse the --extra overlay: a JSON array literal or a path to a JSON file."""
    text = spec if spec.lstrip().startswith("[") else Path(spec).read_text(encoding="utf-8-sig")
    data = json.loads(text)
    if not isinstance(data, list):
        raise ValueError("--extra must be a JSON array of objects")
    rows = []
    for item in data:
        if not isinstance(item, dict):
            raise ValueError(f"each --extra entry must be a JSON object, got: {item!r}")
        row = {k: str(item.get(k) or "").strip() for k in FIELDS}
        row["code"] = str(item.get("code") or "").strip()  # kept for traceability
        rows.append(row)
    return rows


def merge_extra(rows: list[dict], extras: list[dict]) -> list[dict]:
    """Extras replace a catalog row with the same method_name (case-insensitive),
    otherwise append — so overrides can retune shipped methods or grow the catalog.
    A replacement inherits the shipped row's num; appended extras get the next
    free nums, so every merged method stays addressable by number."""
    merged = list(rows)
    index = {r["method_name"].lower(): i for i, r in enumerate(merged)}
    for e in extras:
        key = e["method_name"].lower()
        if key in index:
            e = dict(e)
            e["num"] = e["num"] or merged[index[key]]["num"]

View on GitHub (pinned to b70486b9bd)

Solutions

  1. Read the `{item!r}` value in the message and wrap that element in an object with at least `method_name`, `category`, `description`, and `output_pattern`.
  2. Run the file through `python -c "import json,sys;[print(type(x).__name__,x) for x in json.load(open(sys.argv[1]))]" methods.json` to spot any non-dict element.
  3. Regenerate the overlay from a typed source (CSV/typed config) rather than hand-editing JSON.

Example fix

# before
[
  "Devil's Advocate",
  {"method_name":"Six Hats","category":"..."}
]

# after
[
  {"method_name":"Devil's Advocate","category":"challenge","description":"...","output_pattern":"..."},
  {"method_name":"Six Hats","category":"...","description":"...","output_pattern":"..."}
]
Defensive patterns

Strategy: type-guard

Validate before calling

import json
data = json.loads(spec)
assert isinstance(data, list) and all(isinstance(x, dict) for x in data), \
    'each --extra entry must be a JSON object'

Type guard

def is_object_array(v: object) -> bool:
    return isinstance(v, list) and all(isinstance(x, dict) for x in v)

Try / catch

try:
    extras = load_extra(args.extra)
except ValueError as e:
    print(f"error: {e}", file=sys.stderr); sys.exit(2)

Prevention

When it happens

Trigger: The overlay array contains a bare string, number, boolean, or null among the objects: `["Six Hats", {"method_name":"..."}]`, or a trailing comma / JSON5-style element that deserializes to null.

Common situations: Appending a free-text note into the array by mistake; a truncated copy-paste that drops the braces off one entry; a generator that emits method names as strings instead of objects.

Related errors


AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13). Data as JSON: /api/errors/fc1f4cb40556dd0e. Report an issue: GitHub.