ComposioHQ/composio · error · ValidationError
Ambiguous custom tool slug "${slug}". Multiple custom toolki
Error message
Ambiguous custom tool slug "${slug}". Multiple custom toolkit tools share this original slug; manual session.execute() by original slug is only supported when the original slug is unique.${hint} What it means
When custom toolkits register tools, each gets a final namespaced slug (LOCAL_<TOOLKIT>_<TOOL>) while the original slug stays usable for manual session.execute() only if unique. If two custom toolkits expose the same original slug (case-insensitive), that slug is marked ambiguous and assertUnambiguousCustomToolSlug throws ValidationError to prevent executing an arbitrary toolkit's tool.
Source
Thrown at ts/packages/core/src/models/customToolExecution.ts:49
* Reject ambiguous bare original slugs before falling through to backend execution.
* Agents receive final slugs (LOCAL_<TOOLKIT>_<TOOL>) from schemas, while bare original
* slugs are only a convenience for manual session.execute() calls when unique.
*/
export function assertUnambiguousCustomToolSlug(
map: CustomToolsMap | undefined,
slug: string
): void {
if (!map) return;
const upper = slug.toUpperCase();
if (!map.ambiguousOriginalSlugs?.has(upper)) return;
const finalSlugs = [...map.byFinalSlug.values()]
.filter(entry => entry.handle.slug.toUpperCase() === upper)
.map(entry => entry.finalSlug)
.sort();
const hint = finalSlugs.length ? ` Use one of: ${finalSlugs.join(', ')}.` : '';
throw new ValidationError(
`Ambiguous custom tool slug "${slug}". Multiple custom toolkit tools share this original slug; ` +
`manual session.execute() by original slug is only supported when the original slug is unique.` +
hint
);
}
/**
* Execute a custom tool in-process.
* Validates input via the Zod schema, calls the user's execute function,
* and wraps the result into the standard response format.
*
* Callers provide a pre-built SessionContext (which may include sibling routing).
*/
export async function executeCustomTool(
entry: CustomToolsMapEntry,
arguments_: Record<string, unknown>,
sessionContext: SessionContext,
options?: { signal?: AbortSignal }View on GitHub (pinned to 64b1b85502)
Solutions
- Use the fully-qualified final slug (LOCAL_<TOOLKIT>_<TOOL>) listed in the error hint
- Rename one of the colliding tools in your custom toolkit definition so original slugs are unique
- Dynamically read final slugs from the tool schemas given to the agent instead of hardcoding bare slugs
Example fix
// before
await session.execute('GET_DATA', { userId });
// after (use the namespaced final slug from the error hint)
await session.execute('LOCAL_REPORTS_GET_DATA', { userId }); Defensive patterns
Strategy: validation
Validate before calling
const finalSlug = toolSchemas.find(t => t.slug.toUpperCase().endsWith('_GET_DATA'))?.slug;
if (!finalSlug) throw new Error('No unique tool found');
await session.execute(finalSlug, args); Type guard
const isUnambiguousSlug = (map: CustomToolsMap | undefined, slug: string): boolean => !map || !map.ambiguousOriginalSlugs?.has(slug.toUpperCase());
Try / catch
try { await session.execute(slug, args); } catch (e) { if (e instanceof ValidationError && e.message.includes('Ambiguous custom tool slug')) { /* pick final slug from hint */ } throw e; } Prevention
- Prefer final namespaced slugs (LOCAL_<TOOLKIT>_<TOOL>) in code
- Keep custom tool slugs unique across toolkits
- Derive slugs from tool schemas rather than hardcoding
When it happens
Trigger: Registering two custom toolkits that both define a tool with the same original slug (e.g. 'GET_DATA'), then calling session.execute('GET_DATA') / routing execution with the bare original slug. The error is thrown by execute, routingExecuteFn, and routeMultiExecute before backend execution.
Common situations: Composing multiple third-party/custom toolkits that happen to reuse generic tool names, refactoring one toolkit into two while keeping tool names, or hardcoded slug strings in app code after adding a second toolkit.
Related errors
- Custom tool slug collision: "${finalSlug}" is already regist
- Custom tool slug collision: original slug "${handle.slug}" i
- Custom tool slugs are not supported in preload.tools: ${cust
- Could not determine a home directory to store the Composio c
- Cache directory {directory} is not writable please provide a
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/abaab51f2eca455a.
Report an issue: GitHub.