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
- Make the file root an array: `[ {"category":"...","technique_name":"...","description":"..."}, ... ]`.
- If the content is wrapped under a key (e.g. `{"techniques": [...]}`), unwrap it so the array is at the top level.
- Validate with `python -m json.tool file.json` and confirm the first non-whitespace character is `[`.
- 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
- Make the file root an array; do not wrap the list under a key.
- Confirm the first non-whitespace character is [.
- Generate overlays from customize.toml's additional_techniques.
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
- each --extra entry must be a JSON object, got: {item!r}
- --extra must be a JSON array of objects
- each --extra entry must be a JSON object, got: {item!r}
- unparseable date: {raw!r} (want YYYY[-MM[-DD]])
- keyed array identifier `{candidate}` must not be empty
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/230f570eb71e0a8d.
Report an issue: GitHub.