bmad-code-org/BMAD-METHOD · error · ConfigError
failed to read {path}: {error}
Error message
failed to read {path}: {error} What it means
`load_toml` catches `OSError` (broad: `PermissionError`, `FileNotFoundError` race, `IsADirectoryError`, disk/IO errors) during the `open`/`read` and re-raises as `ConfigError` with the path and OS message. The file exists and is a regular file, but the process cannot read it — almost always a permissions or filesystem problem rather than a content problem.
Source
Thrown at src/scripts/config_utils.py:31
_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__}"
)
if not value:View on GitHub (pinned to b70486b9bd)
Solutions
- Check permissions and ownership: `ls -l <path>` and `id`.
- Fix ownership/permissions: `chmod +r <path>` or `chown <user>:<group> <path>`.
- If on a network mount, remount or refresh credentials and retry.
- Rule out a TOCTOU deletion by confirming no other process removes the file during the run.
Example fix
# before: file owned by root, mode 0600, tool runs as app user sudo chown app:app _bmad/config.toml && chmod 0644 _bmad/config.toml # after: tool can read the layer
Defensive patterns
Strategy: validation
Validate before calling
import os
assert os.access(path, os.R_OK), f'cannot read {path}: check permissions' Try / catch
try:
load_toml(path, required=required)
except ConfigError as e:
print(f"error: {e}", file=sys.stderr); sys.exit(2) Prevention
- Ensure the runtime user owns or can read config files (chmod 0644).
- On network mounts, validate readability before the run.
- Avoid concurrent processes that delete config mid-run.
When it happens
Trigger: The config file is mode `0600` owned by another user; a read-only mount; the file vanished between the `is_file()` check and the `open` (TOCTOU); a locked file on a network share; disk/IO failure.
Common situations: Running the tool as a different user than the one that created the config; CI in a restricted container; a NFS/CIFS mount with stale credentials; a concurrent process deleting the file mid-run.
Related errors
- required TOML file not found: {path}
- TOML layer is not a file: {path}
- ${label} is not readable: ${dirPath}
- ${label} is not readable: ${filePath}
- ${label}: permission denied creating directory: ${dirPath}
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/7bc40b0e5c39ab29.
Report an issue: GitHub.