{"record":{"id":"6a3fbf8ccdd2357c","repo":"github/spec-kit","slug":"integration-must-be-a-mapping-when-present","errorCode":null,"errorMessage":"'integration' must be a mapping when present.","messagePattern":"'integration' must be a mapping when present\\.","errorType":"exception","errorClass":"BundlerError","httpStatus":null,"severity":"error","filePath":"src/specify_cli/bundler/models/manifest.py","lineNumber":131,"sourceCode":"\n        requires_raw = data.get(\"requires\")\n        if requires_raw is None:\n            requires_raw = {}\n        elif not isinstance(requires_raw, dict):\n            raise BundlerError(\"'requires' must be a mapping when present.\")\n        requires = Requires(\n            speckit_version=_text(requires_raw.get(\"speckit_version\")),\n            tools=_parse_str_list(requires_raw.get(\"tools\"), \"requires.tools\"),\n            mcp=_parse_str_list(requires_raw.get(\"mcp\"), \"requires.mcp\"),\n        )\n\n        integration = None\n        integration_raw = data.get(\"integration\")\n        # Mirror the requires/provides guards above: a present-but-non-mapping\n        # 'integration' (e.g. a bare string \"copilot\") was silently dropped,\n        # leaving the bundle wrongly integration-agnostic. Reject it instead.\n        if integration_raw is not None and not isinstance(integration_raw, dict):\n            raise BundlerError(\"'integration' must be a mapping when present.\")\n        if isinstance(integration_raw, dict) and integration_raw.get(\"id\"):\n            integration = IntegrationRef(id=str(integration_raw[\"id\"]).strip())\n\n        provides = data.get(\"provides\")\n        if provides is None:\n            provides = {}\n        elif not isinstance(provides, dict):\n            raise BundlerError(\"'provides' must be a mapping when present.\")\n\n        tags_raw = data.get(\"tags\")\n        if tags_raw is None:\n            tags_raw = []\n        else:\n            tags_raw = _parse_str_list(tags_raw, \"tags\")\n\n        manifest = cls(\n            schema_version=schema_version,\n            bundle=meta,","sourceCodeStart":113,"sourceCodeEnd":149,"githubUrl":"https://github.com/github/spec-kit/blob/bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c/src/specify_cli/bundler/models/manifest.py#L113-L149","documentation":"Raised by BundleManifest.from_dict when an optional 'integration' key is present but is not a mapping. Previously a present-but-non-mapping value (e.g. the bare string 'copilot') was silently dropped, leaving the bundle wrongly integration-agnostic; the guard now rejects it, mirroring the requires/provides guards.","triggerScenarios":"A manifest contains 'integration: copilot' (a string) instead of 'integration: {id: copilot}'. BundleManifest.from_dict sees integration_raw is not None and not a dict and raises.","commonSituations":"Naturally writing the integration name as a bare scalar — the most intuitive shorthand and the exact bug this guard closes; copying integration config from a different schema that uses strings.","solutions":["Wrap the value in a mapping with an 'id' key: 'integration: {id: copilot}'.","Remove the 'integration' key if the bundle is intentionally integration-agnostic."],"exampleFix":"# before (bundle.yml)\nintegration: copilot\n\n# after\nintegration:\n  id: copilot","handlingStrategy":"type-guard","validationCode":"def integration_shape_ok(data: dict) -> bool:\n    i = data.get(\"integration\")\n    return i is None or isinstance(i, dict)","typeGuard":"def is_integration_ref(raw: object) -> bool:\n    return raw is None or (isinstance(raw, dict) and \"id\" in raw)","tryCatchPattern":"try:\n    manifest = BundleManifest.from_file(p)\nexcept BundlerError as e:\n    if \"'integration' must be a mapping\" in str(e):\n        # rewrite 'integration: copilot' as 'integration: {id: copilot}'\n        ...","preventionTips":["Always nest integration identity: 'integration:\\n  id: <key>'.","A bare string used to be silently dropped — never rely on that old behavior.","Omit 'integration' for integration-agnostic bundles."],"tags":["bundler","manifest","yaml","validation","integration"],"backgroundTag":null,"analyzedSha":"bf88c9f9a82fa370c7a7257aa2b3cf10b457b65c","analyzedAt":"2026-08-14T19:43:37.150Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}