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

--extra must be a JSON array of objects

Error message

--extra must be a JSON array of objects

What it means

brain.py's `load_extra` reads a JSON overlay file of additional brainstorming techniques and requires the parsed content to be a top-level array. Unlike pick_methods.py this variant only accepts a file path (no inline literal). The error is raised when `json.loads` succeeds but the value is not a list — typically a single object or a bare scalar. The merged techniques feed every subcommand (categories/list/random/show/html), so a wrong root shape would corrupt the browse page too.

Source

Thrown at src/core-skills/bmad-brainstorming/scripts/brain.py:72

    # utf-8-sig: tolerate BOM-prefixed catalogs (Excel "CSV UTF-8", Notepad)
    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(file: Path) -> list[dict]:
    """Merge-in techniques from a JSON overlay — a list of
    {category, technique_name, description[, detail]} objects. This is how
    customize.toml's `additional_techniques` become first-class across *every*
    subcommand (categories/list/random/show/html), so the browse page and
    category draws include them too, not just the in-chat flows."""
    data = json.loads(file.read_text(encoding="utf-8-sig"))
    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}")
        rows.append({
            "category": str(item.get("category", "")).strip(),
            "technique_name": str(item.get("technique_name", "")).strip(),
            "description": str(item.get("description", "")).strip(),
            "detail": str(item.get("detail") or "").strip(),
            "provenance": str(item.get("provenance") or "").strip(),
            "good_for": str(item.get("good_for") or "").strip(),
            "audience": str(item.get("audience") or "").strip(),
        })
    return rows


def merge_extra(rows: list[dict], extras: list[dict]) -> list[dict]:
    """Extras replace a catalog row with the same technique_name (case-insensitive),

View on GitHub (pinned to b70486b9bd)

Solutions

  1. Make the file root an array: `[ {"category":"...","technique_name":"...","description":"..."}, ... ]`.
  2. If the content is wrapped under a key (e.g. `{"techniques": [...]}`), unwrap it so the array is at the top level.
  3. Validate with `python -m json.tool file.json` and confirm the first non-whitespace character is `[`.
  4. Generate the overlay from `customize.toml`'s `additional_techniques` through the project emitter to guarantee the shape.

Example fix

# before (extra_techniques.json)
{
  "category": "divergent",
  "technique_name": "SCAMPER",
  "description": "..."
}

# after
[
  {
    "category": "divergent",
    "technique_name": "SCAMPER",
    "description": "...",
    "detail": "",
    "provenance": "",
    "good_for": "",
    "audience": ""
  }
]
Defensive patterns

Strategy: validation

Validate before calling

import json
data = json.loads(Path(file).read_text(encoding='utf-8-sig'))
assert isinstance(data, list), '--extra must be a JSON array of objects'

Type guard

def file_is_json_array(path) -> bool:
    try:
        return isinstance(json.loads(Path(path).read_text(encoding='utf-8-sig')), list)
    except (OSError, json.JSONDecodeError):
        return False

Try / catch

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

Prevention

When it happens

Trigger: Pointing `--extra` at a file whose JSON root is an object (`{"category":"...","technique_name":"..."}`) or a string, instead of an array of such objects.

Common situations: Authoring one extra technique and forgetting the array brackets; exporting a single record from a tool that defaults to object serialization; a template that wraps the list under a key like `{"techniques": [...]}`.

Related errors


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