bmad-code-org/BMAD-METHOD · error · ConfigError
TOML layer did not parse to a table: {path}
Error message
TOML layer did not parse to a table: {path} What it means
Defensive guard at the end of `load_toml`: after a successful `tomllib.load`, the result must be a `dict` (a TOML top-level table). `tomllib` always returns a dict for well-formed TOML, so in practice this is nearly unreachable — it would only fire on a future/alternate parser that returns a non-dict root, or if `load_toml` is repurposed with a parser that can yield a bare array. Treat it as an invariant assertion, not an expected user error.
Source
Thrown at src/scripts/config_utils.py:33
def load_toml(path: Path, *, required: bool = False) -> dict[str, Any]:
"""Load a TOML table, allowing absence only for optional layers."""
if not path.exists():
if required:
raise ConfigError(f"required TOML file not found: {path}")
return {}
if not path.is_file():
raise ConfigError(f"TOML layer is not a file: {path}")
try:
with path.open("rb") as stream:
parsed = tomllib.load(stream)
except tomllib.TOMLDecodeError as error:
raise ConfigError(f"failed to parse {path}: {error}") from error
except OSError as error:
raise ConfigError(f"failed to read {path}: {error}") from error
if not isinstance(parsed, dict):
raise ConfigError(f"TOML layer did not parse to a table: {path}")
return parsed
def _detect_keyed_merge_field(items: list[Any]) -> str | None:
if not items or not all(isinstance(item, dict) for item in items):
return None
for candidate in _KEYED_MERGE_FIELDS:
if all(candidate in item for item in items):
for item in items:
value = item[candidate]
if not isinstance(value, str):
raise ConfigError(
f"keyed array identifier `{candidate}` must be a string, "
f"got {type(value).__name__}"
)
if not value:
raise ConfigError(
f"keyed array identifier `{candidate}` must not be empty"View on GitHub (pinned to b70486b9bd)
Solutions
- Confirm you are using stdlib `tomllib` (Python 3.11+) and not a third-party substitute.
- Ensure the file genuinely has key/value tables at the top level, not a bare TOML array document.
- If you wrapped the parser, make sure it still returns a dict root.
Defensive patterns
Strategy: validation
Validate before calling
import tomllib
parsed = tomllib.load(open(path,'rb'))
assert isinstance(parsed, dict), f'TOML layer did not parse to a table: {path}' Type guard
def parses_to_table(path) -> bool:
return isinstance(tomllib.load(open(path,'rb')), dict) Try / catch
try:
load_toml(path, required=required)
except ConfigError as e:
print(f"error: {e}", file=sys.stderr); sys.exit(2) Prevention
- Use stdlib tomllib; do not swap in a parser that can return a non-dict root.
- Keep TOML top-level as key/value tables.
When it happens
Trigger: Swapping `tomllib` for a parser that returns a list for a top-level TOML array; an internal refactor that bypasses the table contract; a corrupt parse path that returns None.
Common situations: Essentially never in normal use. If it appears, suspect a custom monkeypatch of `tomllib` or a hand-edited `config_utils.py`.
Related errors
- required TOML file not found: {path}
- TOML layer is not a file: {path}
- failed to parse {path}: {error}
- keyed array identifier `{candidate}` must be a string, got {
- 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/f22b33dead1f99a7.
Report an issue: GitHub.