OpenBMB/ChatDev · error · ConfigError

tooling requires config block

Error message

tooling requires config block

What it means

ToolingConfig.from_dict requires a nested 'config' block alongside 'type'; if mapping['config'] is absent (None), ConfigError is raised at path '...config'. The type-specific parser needs its payload there.

Source

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

            ChildKey(field="config", value=name): config_cls
            for name, config_cls in iter_tooling_type_registrations().items()
        }

    @classmethod
    def from_dict(cls, data: Mapping[str, Any], *, path: str) -> "ToolingConfig":
        mapping = require_mapping(data, path)
        tooling_type = require_str(mapping, "type", path)
        try:
            config_cls = get_tooling_type_config(tooling_type)
        except RegistryError as exc:
            raise ConfigError(
                f"tooling.type must be one of {list(iter_tooling_type_registrations().keys())}",
                extend_path(path, "type"),
            ) from exc

        config_payload = mapping.get("config")
        if config_payload is None:
            raise ConfigError("tooling requires config block", extend_path(path, "config"))

        config_obj = config_cls.from_dict(config_payload, path=extend_path(path, "config"))

        prefix = optional_str(mapping, "prefix", path)
        return cls(type=tooling_type, config=config_obj, prefix=prefix, path=path)

    @classmethod
    def field_specs(cls) -> Dict[str, ConfigFieldSpec]:
        specs = super().field_specs()
        type_spec = specs.get("type")
        if type_spec:
            registrations = iter_tooling_type_registrations()
            metadata = iter_tooling_type_metadata()
            type_names = list(registrations.keys())
            default_value = type_names[0] if type_names else None
            descriptions = {name: (metadata.get(name) or {}).get("summary") for name in type_names}
            specs["type"] = replace(
                type_spec,

View on GitHub (pinned to 4fb2db0ea9)

Solutions

  1. Add a config block: {"type": "http", "config": {}} (empty dict is accepted)
  2. If defaults are all you need, pass config: {} rather than omitting it
  3. Keep tooling keys nested one level under config

Example fix

// before
{"tooling": {"type": "http"}}
// after
{"tooling": {"type": "http", "config": {}}}
Defensive patterns

Strategy: validation

Validate before calling

tooling = data.get("tooling", {})
if "type" in tooling:
    tooling.setdefault("config", {})

Type guard

def has_config_block(t: dict) -> bool:
    return isinstance(t.get("config"), dict)

Try / catch

except ConfigError as e:
    if "requires config block" in str(e):
        data["tooling"]["config"] = {}
        retry_from_dict(data)

Prevention

When it happens

Trigger: {"tooling": {"type": "http"}} with no config key; config: null; config flattened one level up so its keys sit beside type.

Common situations: Hand-minimal configs omitting defaults; flattening the nesting by mistake when authoring JSON/YAML.

Related errors


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