{"record":{"id":"a1fc656ac9c32ac5","repo":"OpenBMB/ChatDev","slug":"tooling-must-be-a-list","errorCode":null,"errorMessage":"tooling must be a list","messagePattern":"tooling must be a list","errorType":"validation","errorClass":"ConfigError","httpStatus":null,"severity":"error","filePath":"entity/configs/node/agent.py","lineNumber":370,"sourceCode":"        role = optional_str(mapping, \"role\", path)\n        api_key = optional_str(mapping, \"api_key\", path)\n        params = optional_dict(mapping, \"params\", path) or {}\n        raw_input_mode = optional_str(mapping, \"input_mode\", path)\n        input_mode = AgentInputMode.MESSAGES\n        if raw_input_mode:\n            try:\n                input_mode = AgentInputMode(raw_input_mode.strip().lower())\n            except ValueError as exc:\n                raise ConfigError(\n                    \"model.input_mode must be 'prompt' or 'messages'\",\n                    extend_path(path, \"input_mode\"),\n                ) from exc\n\n        tooling_cfg: List[ToolingConfig] = []\n        if \"tooling\" in mapping and mapping[\"tooling\"] is not None:\n            raw_tooling = mapping[\"tooling\"]\n            if not isinstance(raw_tooling, list):\n                 raise ConfigError(\"tooling must be a list\", extend_path(path, \"tooling\"))\n            for idx, item in enumerate(raw_tooling):\n                tooling_cfg.append(\n                    ToolingConfig.from_dict(item, path=extend_path(path, f\"tooling[{idx}]\"))\n                )\n\n        thinking_cfg = None\n        if \"thinking\" in mapping and mapping[\"thinking\"] is not None:\n            thinking_cfg = ThinkingConfig.from_dict(mapping[\"thinking\"], path=extend_path(path, \"thinking\"))\n\n        memories_cfg: List[MemoryAttachmentConfig] = []\n        if \"memories\" in mapping and mapping[\"memories\"] is not None:\n            raw_memories = mapping[\"memories\"]\n            if not isinstance(raw_memories, list):\n                raise ConfigError(\"memories must be a list\", extend_path(path, \"memories\"))\n            for idx, item in enumerate(raw_memories):\n                memories_cfg.append(\n                    MemoryAttachmentConfig.from_dict(item, path=extend_path(path, f\"memories[{idx}]\"))\n                )","sourceCodeStart":352,"sourceCodeEnd":388,"githubUrl":"https://github.com/OpenBMB/ChatDev/blob/4fb2db0ea90375ce1059f44fe03ffbd191a7a169/entity/configs/node/agent.py#L352-L388","documentation":"The 'tooling' key of an agent model config is present and non-null but is not a JSON/YAML list. Tooling entries are parsed as a list of ToolingConfig objects, so any other shape fails immediately.","triggerScenarios":"Passing tooling as a single dict (one tool instead of a list), a string, or a number: \"tooling\": {\"type\": \"web_search\"} instead of [{...}]. A null/absent tooling key is fine.","commonSituations":"Wrapping a single tool config directly instead of in a list — the most common shape mistake; hand-editing YAML and dropping the '- ' list marker.","solutions":["Wrap single tool configs in a list: \"tooling\": [{...}] not {\"tooling\": {...}}","Verify YAML uses dash-prefixed list items under 'tooling'","Omit 'tooling' or set it to null when no tools are needed"],"exampleFix":"// before\n\"tooling\": {\"type\": \"web_search\"}\n// after\n\"tooling\": [{\"type\": \"web_search\"}]","handlingStrategy":"validation","validationCode":"tooling = cfg.get('model', {}).get('tooling')\nif tooling is not None and not isinstance(tooling, list):\n    cfg['model']['tooling'] = [tooling]","typeGuard":"def has_list_tooling(cfg: dict) -> bool:\n    t = cfg.get('model', {}).get('tooling')\n    return t is None or isinstance(t, list)","tryCatchPattern":"try:\n    ModelConfig.from_dict(data, path='agent')\nexcept ConfigError as e:\n    if 'tooling' in e.path and isinstance(data['model'].get('tooling'), dict):\n        data['model']['tooling'] = [data['model']['tooling']]\n        ModelConfig.from_dict(data, path='agent')\n    else:\n        raise","preventionTips":["Always author tooling as a list even for one tool","Linter rule: sequence keys must be lists in config files","Unit-test config round-trips through from_dict"],"tags":["config","agent","tooling","validation","python"],"backgroundTag":"schema-validation-failed","analyzedSha":"4fb2db0ea90375ce1059f44fe03ffbd191a7a169","analyzedAt":"2026-08-27T14:35:29.622Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}