{"record":{"id":"29d28c12babb3d2e","repo":"github/spec-kit","slug":"invalid-requires-expected-a-mapping-got-type-re","errorCode":null,"errorMessage":"Invalid requires: expected a mapping, got {type(requires).__name__}","messagePattern":"Invalid requires: expected a mapping, got (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/extensions/__init__.py","lineNumber":346,"sourceCode":"        # Validate optional category field (free-form string)\n        if \"category\" in ext:\n            if not isinstance(ext[\"category\"], str) or not ext[\"category\"].strip():\n                raise ValidationError(\n                    \"Invalid extension.category: must be a non-empty string\"\n                )\n\n        # Validate optional effect field\n        if \"effect\" in ext:\n            if not isinstance(ext[\"effect\"], str) or ext[\"effect\"] not in VALID_EFFECTS:\n                raise ValidationError(\n                    f\"Invalid extension.effect '{ext.get('effect')}': \"\n                    f\"must be one of {sorted(VALID_EFFECTS)}\"\n                )\n\n        # Validate requires section\n        requires = self.data[\"requires\"]\n        if not isinstance(requires, dict):\n            raise ValidationError(\n                f\"Invalid requires: expected a mapping, got {type(requires).__name__}\"\n            )\n        if \"speckit_version\" not in requires:\n            raise ValidationError(\"Missing requires.speckit_version\")\n        # Presence alone is not enough: check_compatibility() feeds this value to\n        # ``SpecifierSet(required)``, guarded only by ``except InvalidSpecifier``,\n        # which a non-string escapes two different ways. A float/int/bool/None\n        # raises TypeError from the constructor, while a list or dict is an\n        # *iterable*, so SpecifierSet accepts it and the failure surfaces much\n        # later as ``AttributeError: 'str' object has no attribute 'filter'`` from\n        # inside .contains(). Neither is a CompatibilityError, so both bypass the\n        # CLI's \"Compatibility Error\" handler and exit 1 with a raw traceback\n        # naming no field. An unquoted ``speckit_version: 1.0`` is an easy YAML\n        # slip. Mirrors the sibling IntegrationDescriptor, which already requires\n        # a non-empty string here.\n        if (\n            not isinstance(requires[\"speckit_version\"], str)\n            or not requires[\"speckit_version\"].strip()","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/extensions/__init__.py#L328-L364","documentation":"The top-level requires section of extension.yml must be a mapping (YAML dict). The validator indexes self.data[\"requires\"] and checks isinstance(requires, dict); a list, string, or scalar fails immediately.","triggerScenarios":"A manifest has `requires: \"spec-kit>=1.0\"` or `requires:\n  - speckit_version: '>=1.0'` (list of mappings). The isinstance check fails and ValidationError names the actual type.","commonSituations":"Author assumes requires is a list of requirement strings (pip-style) or a single constraint string, instead of a mapping keyed by speckit_version.","solutions":["Rewrite requires as a mapping: `requires:\\n  speckit_version: \">=1.0.0\"`.","Remove stray list dashes or quoting that turns the section into a string.","Model it on a known-good bundled extension manifest (e.g. extensions/agent-context)."],"exampleFix":"# before\nrequires: \">=1.0.0\"\n\n# after\nrequires:\n  speckit_version: \">=1.0.0\"","handlingStrategy":"validation","validationCode":"def requires_ok(data: dict) -> bool:\n    return isinstance(data.get(\"requires\"), dict)","typeGuard":"def is_requires_mapping(data: dict) -> bool:\n    return isinstance(data.get(\"requires\"), dict)","tryCatchPattern":null,"preventionTips":["requires is a mapping, never a list or a bare constraint string.","Keep a known-good manifest next to your authoring docs and diff against it."],"tags":["extension-manifest","validation","yaml-structure"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}