{"record":{"id":"927ca779dbd9fc44","repo":"zylon-ai/private-gpt","slug":"toolset-toolset-name-has-duplicate-tool-names","errorCode":null,"errorMessage":"ToolSet '{toolset.name}' has duplicate tool names","messagePattern":"ToolSet '(.+?)' has duplicate tool names","errorType":"validation","errorClass":"InvalidToolSetError","httpStatus":null,"severity":"error","filePath":"private_gpt/components/toolsets/services/toolset_service.py","lineNumber":23,"sourceCode":"from private_gpt.components.toolsets.errors import InvalidToolSetError\nfrom private_gpt.components.toolsets.models.tool_set import ToolSet\nfrom private_gpt.components.toolsets.repositories.toolset_repository import (\n    ToolSetRepository,\n)\n\n\nclass ToolSetService(BaseModel):\n    \"\"\"Manage registration and retrieval of named toolsets.\"\"\"\n\n    repository: ToolSetRepository\n\n    model_config = ConfigDict(arbitrary_types_allowed=True)\n\n    def register(self, toolset: ToolSet) -> ToolSet:\n        \"\"\"Register one toolset after validating tool name uniqueness.\"\"\"\n        names = [tool.name for tool in toolset.tools]\n        if len(names) != len(set(names)):\n            raise InvalidToolSetError(\n                f\"ToolSet '{toolset.name}' has duplicate tool names\"\n            )\n        return self.repository.save(toolset)\n\n    def get(self, name: str) -> ToolSet | None:\n        \"\"\"Return one toolset by name when it exists.\"\"\"\n        return self.repository.get_by_name(name)\n\n    def list(self) -> list[ToolSet]:\n        \"\"\"Return all registered toolsets.\"\"\"\n        return self.repository.list()\n\n    def delete(self, name: str) -> bool:\n        \"\"\"Delete one toolset by name.\"\"\"\n        return self.repository.delete(name)\n","sourceCodeStart":5,"sourceCodeEnd":39,"githubUrl":"https://github.com/zylon-ai/private-gpt/blob/4a030776a31a901ad80b1bf4d7faa2c1a367efbb/private_gpt/components/toolsets/services/toolset_service.py#L5-L39","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Deduplicate by name before registering: keep one tool per unique name","If two distinct tools share a default name, give one an explicit unique tool.name","Add a uniqueness assertion in the code that builds ToolSet objects so failures surface at assembly time"],"exampleFix":"# before\ntoolset = ToolSet(name=\"default\", tools=[search_tool, search_tool])\n# after\n_dedup = {t.name: t for t in tools}\ntoolset = ToolSet(name=\"default\", tools=list(_dedup.values()))","handlingStrategy":"validation","validationCode":"def validate_toolset(toolset: ToolSet) -> None:\n    names = [t.name for t in toolset.tools]\n    dupes = {n for n in names if names.count(n) > 1}\n    if dupes:\n        raise InvalidToolSetError(f\"duplicate tool names before save: {sorted(dupes)}\")","typeGuard":null,"tryCatchPattern":"try:\n    service.register(toolset)\nexcept InvalidToolSetError:\n    # dedupe by name ({t.name: t for t in tools}) and re-register","preventionTips":["Dedupe tools by name whenever composing toolsets from multiple sources","Use a dict keyed by tool name during assembly so duplicates cannot occur","Reject duplicate tool names in UI forms before submit"],"tags":["tools","validation","uniqueness","toolset"],"backgroundTag":null,"analyzedSha":"4a030776a31a901ad80b1bf4d7faa2c1a367efbb","analyzedAt":"2026-08-15T03:51:26.951Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}