{"record":{"id":"4a3f0e3c4536bb48","repo":"OpenBMB/ChatDev","slug":"tooling-requires-config-block","errorCode":null,"errorMessage":"tooling requires config block","messagePattern":"tooling requires config block","errorType":"validation","errorClass":"ConfigError","httpStatus":null,"severity":"error","filePath":"entity/configs/node/tooling.py","lineNumber":637,"sourceCode":"            ChildKey(field=\"config\", value=name): config_cls\n            for name, config_cls in iter_tooling_type_registrations().items()\n        }\n\n    @classmethod\n    def from_dict(cls, data: Mapping[str, Any], *, path: str) -> \"ToolingConfig\":\n        mapping = require_mapping(data, path)\n        tooling_type = require_str(mapping, \"type\", path)\n        try:\n            config_cls = get_tooling_type_config(tooling_type)\n        except RegistryError as exc:\n            raise ConfigError(\n                f\"tooling.type must be one of {list(iter_tooling_type_registrations().keys())}\",\n                extend_path(path, \"type\"),\n            ) from exc\n\n        config_payload = mapping.get(\"config\")\n        if config_payload is None:\n            raise ConfigError(\"tooling requires config block\", extend_path(path, \"config\"))\n\n        config_obj = config_cls.from_dict(config_payload, path=extend_path(path, \"config\"))\n\n        prefix = optional_str(mapping, \"prefix\", path)\n        return cls(type=tooling_type, config=config_obj, prefix=prefix, path=path)\n\n    @classmethod\n    def field_specs(cls) -> Dict[str, ConfigFieldSpec]:\n        specs = super().field_specs()\n        type_spec = specs.get(\"type\")\n        if type_spec:\n            registrations = iter_tooling_type_registrations()\n            metadata = iter_tooling_type_metadata()\n            type_names = list(registrations.keys())\n            default_value = type_names[0] if type_names else None\n            descriptions = {name: (metadata.get(name) or {}).get(\"summary\") for name in type_names}\n            specs[\"type\"] = replace(\n                type_spec,","sourceCodeStart":619,"sourceCodeEnd":655,"githubUrl":"https://github.com/OpenBMB/ChatDev/blob/4fb2db0ea90375ce1059f44fe03ffbd191a7a169/entity/configs/node/tooling.py#L619-L655","documentation":"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.","triggerScenarios":"{\"tooling\": {\"type\": \"http\"}} with no config key; config: null; config flattened one level up so its keys sit beside type.","commonSituations":"Hand-minimal configs omitting defaults; flattening the nesting by mistake when authoring JSON/YAML.","solutions":["Add a config block: {\"type\": \"http\", \"config\": {}} (empty dict is accepted)","If defaults are all you need, pass config: {} rather than omitting it","Keep tooling keys nested one level under config"],"exampleFix":"// before\n{\"tooling\": {\"type\": \"http\"}}\n// after\n{\"tooling\": {\"type\": \"http\", \"config\": {}}}","handlingStrategy":"validation","validationCode":"tooling = data.get(\"tooling\", {})\nif \"type\" in tooling:\n    tooling.setdefault(\"config\", {})","typeGuard":"def has_config_block(t: dict) -> bool:\n    return isinstance(t.get(\"config\"), dict)","tryCatchPattern":"except ConfigError as e:\n    if \"requires config block\" in str(e):\n        data[\"tooling\"][\"config\"] = {}\n        retry_from_dict(data)","preventionTips":["Always pair type with config, even if empty {}","Schema-validate required nested fields"],"tags":["config","validation","tooling","missing-field"],"backgroundTag":"missing-required-config-field","analyzedSha":"4fb2db0ea90375ce1059f44fe03ffbd191a7a169","analyzedAt":"2026-08-27T14:35:29.622Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}