ComposioHQ/composio · error · ValidationError

Custom tool slug collision: "${finalSlug}" is already regist

Error message

Custom tool slug collision: "${finalSlug}" is already registered.

What it means

While building the internal custom-tools map, Composio detected that two custom tools resolve to the same final slug (compared case-insensitively). The SDK registers every custom tool under its final slug and cannot disambiguate duplicates, so it throws a ValidationError during map construction rather than failing later at execution time.

Source

Thrown at ts/packages/core/src/models/CustomTool.ts:349

  const byToolkitAndOriginalSlug = new Map<string, CustomToolsMapEntry>();
  const ambiguousOriginalSlugs = new Set<string>();

  const addEntry = (handle: CustomTool, finalSlug: string, toolkit?: string) => {
    const originalSlug = handle.slug.toUpperCase();
    // Custom tool slugs are matched case-insensitively across local and response maps.
    const finalSlugKey = finalSlug.toUpperCase();

    // Length validated early in createCustomTool/createCustomToolkit, but check as safety net
    if (finalSlug.length > MAX_SLUG_LENGTH) {
      throw new ValidationError(
        `Custom tool slug "${handle.slug}" produces final slug "${finalSlug}" ` +
          `which exceeds ${MAX_SLUG_LENGTH} characters.`
      );
    }

    // Check cross-group collisions on final slug
    if (byFinalSlug.has(finalSlugKey)) {
      throw new ValidationError(
        `Custom tool slug collision: "${finalSlug}" is already registered.`
      );
    }

    const qualifiedKey = qualifiedOriginalSlugKey(toolkit, originalSlug);
    if (byToolkitAndOriginalSlug.has(qualifiedKey)) {
      throw new ValidationError(
        `Custom tool slug collision: original slug "${handle.slug}" is already registered for toolkit "${toolkit ?? 'custom'}".`
      );
    }

    const entry: CustomToolsMapEntry = { handle, finalSlug, toolkit };
    byFinalSlug.set(finalSlugKey, entry);
    byToolkitAndOriginalSlug.set(qualifiedKey, entry);
    addOriginalSlugAlias({ byOriginalSlug, ambiguousOriginalSlugs, originalSlug, entry });
  };

  // Process standalone tools

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Uniquely rename the colliding custom tool slug (check case-insensitive equality)
  2. List all registered custom tool slugs and diff them to find the duplicate pair
  3. If the duplicate comes from a custom toolkit, change the toolkit name or tool slug so the final slug differs

Example fix

// before
const tools = [defineTool({ slug: 'fetchPage' }), defineTool({ slug: 'fetch_page' })];
// after
const tools = [defineTool({ slug: 'fetchPage' }), defineTool({ slug: 'extractLinks' })];
Defensive patterns

Strategy: validation

Validate before calling

const final = (slug) => slug.toUpperCase(); // approximate SDK normalization
const seen = new Set<string>();
for (const t of customTools) {
  const key = final(t.slug);
  if (seen.has(key)) throw new Error(`Duplicate final slug: ${t.slug}`);
  seen.add(key);
}

Type guard

const isUniqueFinalSlugs = (tools: {slug: string}[]): boolean =>
  new Set(tools.map(t => t.slug.toUpperCase())).size === tools.length;

Try / catch

try { buildCustomToolsMap(tools); } catch (e) { if (e instanceof ValidationError && e.message.includes('slug collision')) { /* dedupe and retry */ } throw e; }

Prevention

When it happens

Trigger: Registering two custom tools (via Composio custom tool definitions or custom toolkits) whose slugs collide after normalization — e.g. 'My_Tool' and 'my-tool', or a custom tool whose slug matches a toolkit-prefixed slug that reduces to the same final key. Surfaced from buildCustomToolsMap / buildCustomToolsMapFromResponse.

Common situations: Copying a custom tool definition and forgetting to change the slug; mixing SDK custom tools with tools loaded from a response that share names; case differences in slugs across environments; adding a tool to a toolkit that already exposes a similarly-named tool.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/ee5b10d4c5f299ba. Report an issue: GitHub.