{"record":{"id":"1f5f100d6eb072d3","repo":"OpenBMB/ChatDev","slug":"expected-list-of-skill-entries","errorCode":null,"errorMessage":"expected list of skill entries","messagePattern":"expected list of skill entries","errorType":"validation","errorClass":"ConfigError","httpStatus":null,"severity":"error","filePath":"entity/configs/node/skills.py","lineNumber":161,"sourceCode":"    }\n\n    @classmethod\n    def from_dict(cls, data: Mapping[str, Any], *, path: str) -> \"AgentSkillsConfig\":\n        mapping = require_mapping(data, path)\n        enabled = optional_bool(mapping, \"enabled\", path, default=False)\n        if enabled is None:\n            enabled = False\n\n        allow = cls._coerce_allow_entries(mapping.get(\"allow\"), field_path=extend_path(path, \"allow\"))\n\n        return cls(enabled=enabled, allow=allow, path=path)\n\n    @staticmethod\n    def _coerce_allow_entries(value: Any, *, field_path: str) -> List[str]:\n        if value is None:\n            return []\n        if not isinstance(value, list):\n            raise ConfigError(\"expected list of skill entries\", field_path)\n\n        result: List[str] = []\n        for idx, item in enumerate(value):\n            item_path = f\"{field_path}[{idx}]\"\n            if isinstance(item, str):\n                normalized = item.strip()\n                if normalized:\n                    result.append(normalized)\n                continue\n            if isinstance(item, Mapping):\n                entry = AgentSkillSelectionConfig.from_dict(item, path=item_path)\n                result.append(entry.name)\n                continue\n            raise ConfigError(\"expected skill entry mapping or string\", item_path)\n        return result\n","sourceCodeStart":143,"sourceCodeEnd":177,"githubUrl":"https://github.com/OpenBMB/ChatDev/blob/4fb2db0ea90375ce1059f44fe03ffbd191a7a169/entity/configs/node/skills.py#L143-L177","documentation":"_coerce_allow_entries accepts None (empty list) or a list for the skills allow field; any other top-level type (string, mapping, int) raises ConfigError('expected list of skill entries') at the field path.","triggerScenarios":"Setting allow: my-skill (a single string) or allow: {name: my-skill} (a single mapping) instead of a list.","commonSituations":"Shorthand habits from other config systems that accept a single value; JSON tools collapsing one-element arrays; templating emitting a scalar.","solutions":["Wrap the value in a list: allow: [my-skill]","Remove the key entirely if no skills should be allowed (treated as [])","Check JSON producers preserve arrays for single elements"],"exampleFix":"# before\nallow: my-skill\n# after\nallow:\n  - my-skill","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"def is_valid_allow(v) -> bool:\n    return v is None or isinstance(v, list)","tryCatchPattern":"try:\n    SkillsConfig.from_dict(d, path='skills')\nexcept ConfigError as e:\n    if 'expected list of skill entries' in str(e):\n        d['allow'] = [d['allow']] if not isinstance(d.get('allow'), list) else d['allow']\n        SkillsConfig.from_dict(d, path='skills')","preventionTips":["Always emit arrays for allow lists, even single-element","Check JSON producers don't collapse one-element arrays"],"tags":["skills","config","validation"],"backgroundTag":"invalid-config-value-type","analyzedSha":"4fb2db0ea90375ce1059f44fe03ffbd191a7a169","analyzedAt":"2026-08-27T14:35:29.622Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}