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

failed to parse {path}: {error}

Error message

failed to parse {path}: {error}

What it means

`load_toml` catches `tomllib.TOMLDecodeError` and re-raises it as `ConfigError` with the file path and the underlying parse error. It means the TOML file was read from disk but its syntax is invalid — unclosed string, duplicate keys, a bare value, an unterminated table header, a malformed array, or a non-TOML file (YAML/JSON) that was placed there by mistake.

Source

Thrown at src/scripts/config_utils.py:29

    """Raised when a present configuration layer cannot be used safely."""


_KEYED_MERGE_FIELDS = ("code", "id")


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__}"

View on GitHub (pinned to b70486b9bd)

Solutions

  1. Read the full error text after the colon — it includes the line/column of the parse failure.
  2. Open the file at that line and fix the syntax (quote string values, close brackets, deduplicate keys).
  3. Validate with a TOML linter: `python -c "import tomllib;tomllib.load(open('<path>','rb'))"`.
  4. If the file is actually YAML/JSON, convert it to TOML or move it to the correct extension/layer.

Example fix

# before (config.toml)
[project]
name = BMAD          # bare value -> TOMLDecodeError
version = 1.0.       # trailing dot

# after
[project]
name = "BMAD"
version = "1.0"
Defensive patterns

Strategy: try-catch

Validate before calling

import tomllib
try:
    tomllib.load(open(path,'rb'))
except tomllib.TOMLDecodeError as e:
    raise SystemExit(f'TOML syntax error: {e}')

Try / catch

try:
    load_toml(path, required=required)
except ConfigError as e:
    print(f"error: {e}", file=sys.stderr); sys.exit(2)

Prevention

When it happens

Trigger: A hand-edited `_bmad/config.toml` with a syntax slip (`name = BMAD` instead of `name = "BMAD"`); a YAML or JSON file saved with a `.toml` extension; a merge conflict marker left in the file; an unterminated multiline string.

Common situations: Editing config without a TOML-aware editor; copying a snippet from docs that used a different config language; unresolved `<<<<<<<` conflict markers after a git merge.

Understand the failure class

Related errors


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