[6.11.0] config_utils.py: keyed-array merge raises ConfigError on non-string `code`/`id` values instead of falling back — regression vs 6.10, aborts the entire config load

Open Beginner friendly
#2,721 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Quiet
Tech stack
python
Domain
tooling

Research direction

Read _bmad/scripts/config_utils.py, especially _detect_keyed_merge_field around line 45, then run the structural_merge reproduction from the issue. Make non-string candidate values skip to the next candidate and use append semantics when none qualifies, while retaining validation for keyed arrays whose values are all strings.

Written by the indexing model from the issue text.

Description

Severity: medium-high — one user-authored array in any config layer breaks resolve_config, resolve_customization, and render_skill for every skill at once
Version: bmad-method 6.11.0

Summary

_bmad/scripts/config_utils.py's _detect_keyed_merge_field (line ~45) raises as soon as all items of an array-of-tables share a code (or id) field whose value is non-string:

ConfigError: keyed array identifier `code` must be a string, got int

The raise sits inside the first candidate's validation loop, so it fires even when every item also carries a perfectly valid string id — the detector never advances to the next candidate key, and never reaches the append fallback.

6.10's detector (resolve_config.py, _detect_keyed_merge_field, if all(item.get(candidate) is not None for item in items): return candidate) had no type check and merged the same inputs without error. So legal TOML that worked under 6.10 — e.g. HTTP-status tables [{code = 200, ...}], numeric-id rosters — now aborts load_central_config/load_customization entirely, which takes down all three consumer scripts for every skill, not just the one owning the array.

Repro

from config_utils import structural_merge
structural_merge({"arr": [{"code": 1, "id": "a", "v": 1}]},
                 {"arr": [{"code": 2, "id": "b", "v": 2}]})
# ConfigError: keyed array identifier `code` must be a string, got int
# (never tries "id", never falls back to append)

Suggested fix

Treat a non-string candidate value as "this candidate is not the merge key": skip to the next candidate, and fall back to append semantics when no candidate qualifies — matching 6.10's accepting behavior while keeping the new validation for arrays that genuinely opt into keyed merge (all values present and strings).

Dominant language
Python
Stars
53.2k
Forks
6k
Avg merge
4h 26m
Merged PRs (30d)
86

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from bmad-code-org/BMAD-METHOD

All issues in bmad-code-org/BMAD-METHOD

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.