rohitg00/ai-engineering-from-scratch · error · ValueError

policy must be prefix-on-collision or reject

Error message

policy must be prefix-on-collision or reject

What it means

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.

Source

Thrown at phases/13-tools-and-protocols/08-building-an-mcp-client/code/main.py:508

        if kind != "result":
            raise RuntimeError(f"{peer.name}: RPC error {payload}")
        result = dict(payload)
        if peer.era == "modern" and "resultType" not in result:
            raise RuntimeError(f"{peer.name}: modern result omitted resultType")
        if peer.era == "legacy":
            result.setdefault("resultType", "complete")
        return result

    def discover_tools(self) -> None:
        for peer_name in sorted(self.peers):
            peer = self.peers[peer_name]
            if peer.available:
                result = self._request(peer, "tools/list", {})
                peer.tools = sorted(result.get("tools", []), key=lambda tool: tool["name"])

    def merge(self, policy: str = "prefix-on-collision") -> None:
        if policy not in {"prefix-on-collision", "reject"}:
            raise ValueError("policy must be prefix-on-collision or reject")
        self.registry.clear()
        for peer_name in sorted(self.peers):
            peer = self.peers[peer_name]
            for tool in peer.tools:
                local_name = tool["name"]
                canonical_name = local_name
                if canonical_name in self.registry:
                    if policy == "reject":
                        continue
                    canonical_name = f"{peer.name}/{local_name}"
                    if canonical_name in self.registry:
                        raise ValueError(f"canonical collision: {canonical_name}")
                self.registry[canonical_name] = MergedTool(
                    canonical_name=canonical_name,
                    peer_name=peer.name,
                    local_name=local_name,
                    description=tool.get("description", ""),
                )

View on GitHub (pinned to 39ea8a1c6d)

Solutions

  1. Pass exactly 'prefix-on-collision' or 'reject'
  2. Omit the argument to use the default 'prefix-on-collision'
  3. Centralize policy names as constants to avoid typos

Example fix

// before
client.merge("prefix_on_collision")

// after
client.merge("prefix-on-collision")
Defensive patterns

Strategy: validation

Validate before calling

POLICIES = {'prefix-on-collision', 'reject'}
assert policy in POLICIES, f'policy must be one of {POLICIES}'
client.merge(policy)

Type guard

def is_merge_policy(value) -> bool:
    return value in {'prefix-on-collision', 'reject'}

Try / catch

null

Prevention

When it happens

Trigger: Calling client.merge(policy) with a value not in {'prefix-on-collision', 'reject'}.

Common situations: Typo'd policy names; passing None or an empty string expecting a default; copy-pasting a policy constant from another library.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26). Data as JSON: /api/errors/ec9cd2c63e418703. Report an issue: GitHub.