OpenBMB/ChatDev · error · ConfigError

env must be a mapping

Error message

env must be a mapping

What it means

Raised when the stdio tooling config 'env' key exists but is not a mapping/dict. The env dict is normalized to str->str; any non-mapping (list, string, scalar) is rejected.

Source

Thrown at entity/configs/node/tooling.py:523

        mapping = require_mapping(data, path)
        command = require_str(mapping, "command", path)
        args_raw = ensure_list(mapping.get("args"))
        normalized_args: List[str] = []
        for idx, arg in enumerate(args_raw):
            arg_path = extend_path(path, f"args[{idx}]")
            if not isinstance(arg, str):
                raise ConfigError("args entries must be strings", arg_path)
            normalized_args.append(arg)

        cwd = optional_str(mapping, "cwd", path)
        inherit_env = optional_bool(mapping, "inherit_env", path, default=True)
        if inherit_env is None:
            inherit_env = True

        env_mapping = mapping.get("env")
        if env_mapping is not None:
            if not isinstance(env_mapping, Mapping):
                raise ConfigError("env must be a mapping", extend_path(path, "env"))
            env = {str(k): str(v) for k, v in env_mapping.items()}
        else:
            env = {}

        timeout_value = mapping.get("startup_timeout", 10.0)
        if timeout_value is None:
            startup_timeout = 10.0
        elif isinstance(timeout_value, (int, float)):
            startup_timeout = float(timeout_value)
        else:
            raise ConfigError("startup_timeout must be numeric", extend_path(path, "startup_timeout"))

        wait_for_log = optional_str(mapping, "wait_for_log", path)
        cache_ttl_value = mapping.get("cache_ttl", 0.0)
        if cache_ttl_value is None:
            cache_ttl = 0.0
        elif isinstance(cache_ttl_value, (int, float)):
            cache_ttl = float(cache_ttl_value)

View on GitHub (pinned to 4fb2db0ea9)

Solutions

  1. Convert env to a mapping: env: {FOO: bar, BAZ: qux}
  2. If your source is shell-style assignments, parse them into a dict first (dict(x.split('=',1) for x in pairs))
  3. Remove env entirely to inherit/process-default to {}

Example fix

# before
env:
  - FOO=bar
# after
env:
  FOO: bar
Defensive patterns

Strategy: type-guard

Validate before calling

env = cfg.get("env")
assert env is None or isinstance(env, dict)

Type guard

from collections.abc import Mapping
def valid_env(v) -> bool:
    return v is None or isinstance(v, Mapping)

Try / catch

except ConfigError as e:
    if "env must be a mapping" in str(e):
        cfg["env"] = dict(x.split("=", 1) for x in cfg["env"])  # if list of K=V

Prevention

When it happens

Trigger: env: ["FOO=bar"] (list of assignments) instead of env: {FOO: bar}; env: "FOO=bar" as a plain string; YAML indentation making env a list.

Common situations: Copying docker-run style -e FOO=bar syntax into the config; shell-style env strings not converted to dicts.

Related errors


AI-assisted analysis of OpenBMB/ChatDev@4fb2db0ea9 (2026-08-27). Data as JSON: /api/errors/58dc3c817ed31090. Report an issue: GitHub.