{"record":{"id":"ec9cd2c63e418703","repo":"rohitg00/ai-engineering-from-scratch","slug":"policy-must-be-prefix-on-collision-or-reject","errorCode":null,"errorMessage":"policy must be prefix-on-collision or reject","messagePattern":"policy must be prefix-on-collision or reject","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py","lineNumber":508,"sourceCode":"        if kind != \"result\":\n            raise RuntimeError(f\"{peer.name}: RPC error {payload}\")\n        result = dict(payload)\n        if peer.era == \"modern\" and \"resultType\" not in result:\n            raise RuntimeError(f\"{peer.name}: modern result omitted resultType\")\n        if peer.era == \"legacy\":\n            result.setdefault(\"resultType\", \"complete\")\n        return result\n\n    def discover_tools(self) -> None:\n        for peer_name in sorted(self.peers):\n            peer = self.peers[peer_name]\n            if peer.available:\n                result = self._request(peer, \"tools/list\", {})\n                peer.tools = sorted(result.get(\"tools\", []), key=lambda tool: tool[\"name\"])\n\n    def merge(self, policy: str = \"prefix-on-collision\") -> None:\n        if policy not in {\"prefix-on-collision\", \"reject\"}:\n            raise ValueError(\"policy must be prefix-on-collision or reject\")\n        self.registry.clear()\n        for peer_name in sorted(self.peers):\n            peer = self.peers[peer_name]\n            for tool in peer.tools:\n                local_name = tool[\"name\"]\n                canonical_name = local_name\n                if canonical_name in self.registry:\n                    if policy == \"reject\":\n                        continue\n                    canonical_name = f\"{peer.name}/{local_name}\"\n                    if canonical_name in self.registry:\n                        raise ValueError(f\"canonical collision: {canonical_name}\")\n                self.registry[canonical_name] = MergedTool(\n                    canonical_name=canonical_name,\n                    peer_name=peer.name,\n                    local_name=local_name,\n                    description=tool.get(\"description\", \"\"),\n                )","sourceCodeStart":490,"sourceCodeEnd":526,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py#L490-L526","documentation":"merge() only accepts the two collision policies 'prefix-on-collision' and 'reject'. Any other string (including typos like 'prefix_on_collision' or 'Reject') is rejected up front with a ValueError before any registry work happens.","triggerScenarios":"Calling client.merge(policy) with a value not in {'prefix-on-collision', 'reject'}.","commonSituations":"Typo'd policy names; passing None or an empty string expecting a default; copy-pasting a policy constant from another library.","solutions":["Pass exactly 'prefix-on-collision' or 'reject'","Omit the argument to use the default 'prefix-on-collision'","Centralize policy names as constants to avoid typos"],"exampleFix":"// before\nclient.merge(\"prefix_on_collision\")\n\n// after\nclient.merge(\"prefix-on-collision\")","handlingStrategy":"validation","validationCode":"POLICIES = {'prefix-on-collision', 'reject'}\nassert policy in POLICIES, f'policy must be one of {POLICIES}'\nclient.merge(policy)","typeGuard":"def is_merge_policy(value) -> bool:\n    return value in {'prefix-on-collision', 'reject'}","tryCatchPattern":"null","preventionTips":["Use module-level constants for policy names","Validate config strings at load time, not call time"],"tags":["mcp","argument-validation","registry"],"backgroundTag":"invalid-argument-value","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}