{"record":{"id":"3c2f4aa3ce95acdb","repo":"ComposioHQ/composio","slug":"custom-tool-slug-collision-original-slug-handl","errorCode":null,"errorMessage":"Custom tool slug collision: original slug \"${handle.slug}\" is already registered for toolkit \"${toolkit ?? 'custom'}\".","messagePattern":"Custom tool slug collision: original slug \"(.+?)\" is already registered for toolkit \"(.+?)\"\\.","errorType":"validation","errorClass":"ValidationError","httpStatus":null,"severity":"error","filePath":"ts/packages/core/src/models/CustomTool.ts","lineNumber":356,"sourceCode":"\n    // Length validated early in createCustomTool/createCustomToolkit, but check as safety net\n    if (finalSlug.length > MAX_SLUG_LENGTH) {\n      throw new ValidationError(\n        `Custom tool slug \"${handle.slug}\" produces final slug \"${finalSlug}\" ` +\n          `which exceeds ${MAX_SLUG_LENGTH} characters.`\n      );\n    }\n\n    // Check cross-group collisions on final slug\n    if (byFinalSlug.has(finalSlugKey)) {\n      throw new ValidationError(\n        `Custom tool slug collision: \"${finalSlug}\" is already registered.`\n      );\n    }\n\n    const qualifiedKey = qualifiedOriginalSlugKey(toolkit, originalSlug);\n    if (byToolkitAndOriginalSlug.has(qualifiedKey)) {\n      throw new ValidationError(\n        `Custom tool slug collision: original slug \"${handle.slug}\" is already registered for toolkit \"${toolkit ?? 'custom'}\".`\n      );\n    }\n\n    const entry: CustomToolsMapEntry = { handle, finalSlug, toolkit };\n    byFinalSlug.set(finalSlugKey, entry);\n    byToolkitAndOriginalSlug.set(qualifiedKey, entry);\n    addOriginalSlugAlias({ byOriginalSlug, ambiguousOriginalSlugs, originalSlug, entry });\n  };\n\n  // Process standalone tools\n  for (const handle of tools) {\n    addEntry(handle, buildFinalSlug(handle.slug, handle.extendsToolkit), handle.extendsToolkit);\n  }\n\n  // Process toolkit tools\n  if (toolkits) {\n    for (const toolkit of toolkits) {","sourceCodeStart":338,"sourceCodeEnd":374,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/ts/packages/core/src/models/CustomTool.ts#L338-L374","documentation":"The custom-tools map keys original slugs per toolkit, and two entries share the same (toolkit, originalSlug) pair. Even if their final slugs differ, the SDK cannot tell which handle a response tool refers to, so it rejects the duplicate original slug with a ValidationError.","triggerScenarios":"Registering two custom tools with the same slug inside the same toolkit (or with no toolkit, i.e. the 'custom' pseudo-toolkit) via buildCustomToolsMap; e.g. two defineTool calls with slug 'sendEmail' and no toolkit set.","commonSituations":"Accidentally registering the same tool twice (e.g. importing a shared definition into two composable configs); merging custom toolkit definitions that each declare the same tool slug; copy-paste of tool definitions without slug changes.","solutions":["Deduplicate the registration — register each (toolkit, slug) pair only once","Give one of the tools a distinct slug within that toolkit","If two toolkits legitimately have the same slug, ensure each declares its own toolkit name so the qualified key differs"],"exampleFix":"// before\ncomposio.tools.add(defineTool({ slug: 'syncUser', toolkit: 'crm' }));\ncomposio.tools.add(defineTool({ slug: 'syncUser', toolkit: 'crm' }));\n// after\ncomposio.tools.add(defineTool({ slug: 'syncUser', toolkit: 'crm' }));\ncomposio.tools.add(defineTool({ slug: 'archiveUser', toolkit: 'crm' }));","handlingStrategy":"validation","validationCode":"const keys = new Set<string>();\nfor (const t of customTools) {\n  const k = `${t.toolkit ?? 'custom'}:${t.slug}`;\n  if (keys.has(k)) throw new Error(`Duplicate (toolkit, slug): ${k}`);\n  keys.add(k);\n}","typeGuard":"const hasUniqueQualifiedSlugs = (tools: {slug: string; toolkit?: string}[]): boolean =>\n  new Set(tools.map(t => `${t.toolkit ?? 'custom'}:${t.slug}`)).size === tools.length;","tryCatchPattern":"try { await session.withCustomTools(tools); } catch (e) { if (e instanceof ValidationError && /original slug/.test(e.message)) { /* rename duplicate */ } throw e; }","preventionTips":["Never register the same tool definition twice from composable configs","Namespace custom toolkits distinctly","Unit-test your tool registry for duplicates at boot"],"tags":["custom-tools","slug-collision","validation","typescript"],"backgroundTag":"duplicate-resource-identifier","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}