zylon-ai/private-gpt · error · InvalidToolSetError

ToolSet '{toolset.name}' has duplicate tool names

Error message

ToolSet '{toolset.name}' has duplicate tool names

What it means

InvalidToolSetError raised by ToolSetService.register when the toolset being saved contains two tools with the same name. Names must be unique within a toolset because tools are dispatched by name.

Source

Thrown at private_gpt/components/toolsets/services/toolset_service.py:23

from private_gpt.components.toolsets.errors import InvalidToolSetError
from private_gpt.components.toolsets.models.tool_set import ToolSet
from private_gpt.components.toolsets.repositories.toolset_repository import (
    ToolSetRepository,
)


class ToolSetService(BaseModel):
    """Manage registration and retrieval of named toolsets."""

    repository: ToolSetRepository

    model_config = ConfigDict(arbitrary_types_allowed=True)

    def register(self, toolset: ToolSet) -> ToolSet:
        """Register one toolset after validating tool name uniqueness."""
        names = [tool.name for tool in toolset.tools]
        if len(names) != len(set(names)):
            raise InvalidToolSetError(
                f"ToolSet '{toolset.name}' has duplicate tool names"
            )
        return self.repository.save(toolset)

    def get(self, name: str) -> ToolSet | None:
        """Return one toolset by name when it exists."""
        return self.repository.get_by_name(name)

    def list(self) -> list[ToolSet]:
        """Return all registered toolsets."""
        return self.repository.list()

    def delete(self, name: str) -> bool:
        """Delete one toolset by name."""
        return self.repository.delete(name)

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Deduplicate by name before registering: keep one tool per unique name
  2. If two distinct tools share a default name, give one an explicit unique tool.name
  3. Add a uniqueness assertion in the code that builds ToolSet objects so failures surface at assembly time

Example fix

# before
toolset = ToolSet(name="default", tools=[search_tool, search_tool])
# after
_dedup = {t.name: t for t in tools}
toolset = ToolSet(name="default", tools=list(_dedup.values()))
Defensive patterns

Strategy: validation

Validate before calling

def validate_toolset(toolset: ToolSet) -> None:
    names = [t.name for t in toolset.tools]
    dupes = {n for n in names if names.count(n) > 1}
    if dupes:
        raise InvalidToolSetError(f"duplicate tool names before save: {sorted(dupes)}")

Try / catch

try:
    service.register(toolset)
except InvalidToolSetError:
    # dedupe by name ({t.name: t for t in tools}) and re-register

Prevention

When it happens

Trigger: Calling register() on a ToolSet whose tools list has duplicate name values (e.g. two entries both named 'search'); constructing a toolset from a list where defaults collapsed to the same tool name (tool.name or fallback applied twice).

Common situations: Programmatically composing toolsets from multiple sources without deduplication; UI letting users add the same tool twice; merging toolsets that each contain a common default tool.

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/927ca779dbd9fc44. Report an issue: GitHub.