cocoindex-io/cocoindex · error · KeyError

{name}

Error message

{name}

What it means

DedupMap.canonical_of() walks the dedup chain to find the canonical name for an entity. If the name was never registered in the dedup map, a KeyError(name) is raised. The map is acyclic by construction, so lookup terminates.

Source

Thrown at python/cocoindex/ops/entity_resolution/__init__.py:144

    """Result of entity resolution.

    Wraps the underlying ``name -> canonical | None`` dedup map and provides
    safe chain-walking. Treat as read-only; mutations are not part of the
    contract.
    """

    _dedup: dict[str, str | None]

    def canonical_of(self, name: str) -> str:
        """Return the canonical name for ``name``.

        Returns ``name`` itself if it is already canonical. Raises
        :exc:`KeyError` if unknown. Terminates without cycle detection
        because the dedup map is acyclic by construction (see
        requirement.md § "Acyclic by construction").
        """
        if name not in self._dedup:
            raise KeyError(name)
        current = name
        while True:
            target = self._dedup[current]
            if target is None:
                return current
            current = target

    def canonicals(self) -> set[str]:
        """Set of all canonical names (entries whose value is None)."""
        return {name for name, target in self._dedup.items() if target is None}

    def groups(self) -> dict[str, set[str]]:
        """Map each canonical name to the set of all names that resolve to
        it (including itself)."""
        out: dict[str, set[str]] = {c: {c} for c in self.canonicals()}
        for name in self._dedup:
            out[self.canonical_of(name)].add(name)
        return out

View on GitHub (pinned to e84aa99b32)

Solutions

  1. Register the name in the dedup map before calling canonical_of.
  2. Normalize names (strip/case-fold) before both registration and lookup.
  3. Wrap the call in try/except KeyError and treat the name as its own canonical form.

Example fix

// before
canonical = mapper.canonical_of(name)
// after
try:
    canonical = mapper.canonical_of(name.strip().casefold())
except KeyError:
    canonical = name
Defensive patterns

Strategy: try-catch

Validate before calling

if name not in mapper._dedup:
    canonical = name  # or register it first

Try / catch

try:
    canonical = mapper.canonical_of(name)
except KeyError:
    canonical = name  # unknown names are their own canonical form

Prevention

When it happens

Trigger: Calling canonical_of(name) (directly or via create_person_relations / groups) with a name that was never added to the dedup map via the merge/record API.

Common situations: Case/whitespace mismatches between the queried name and the registered one; processing a new entity batch before registering its aliases; stale map from a previous run.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of cocoindex-io/cocoindex@e84aa99b32 (2026-09-08). Data as JSON: /api/errors/cbbfc9932b6ed221. Report an issue: GitHub.