ComposioHQ/composio · error · ValidationError
Custom tool slugs are not supported in preload.tools: ${cust
Error message
Custom tool slugs are not supported in preload.tools: ${customPreloadSlugs.join(', ')}. Set preload: true on the SDK custom tool or custom toolkit definition instead. What it means
The SDK rejects session preload configurations that name custom tools directly in preload.tools. Custom tools must opt into preloading via `preload: true` on their own definition, because the preload list only accepts backend tool slugs.
Source
Thrown at ts/packages/core/src/models/CustomTool.ts:631
): void {
if (!preloadTools || preloadTools === PRELOAD_TOOLS_ALL) {
return;
}
const customPreloadSlugs = preloadTools.filter(slug => {
const normalized = slug.toUpperCase();
return (
// Top-level preload.tools is only for Composio-managed tool slugs.
// Custom tools use `preload: true` on their SDK definitions instead.
normalized.startsWith(LOCAL_TOOL_PREFIX) ||
customToolsMap?.byOriginalSlug.has(normalized) ||
customToolsMap?.ambiguousOriginalSlugs?.has(normalized) ||
customToolsMap?.byFinalSlug.has(normalized)
);
});
if (customPreloadSlugs.length) {
throw new ValidationError(
`Custom tool slugs are not supported in preload.tools: ${customPreloadSlugs.join(
', '
)}. Set preload: true on the SDK custom tool or custom toolkit definition instead.`
);
}
}
/**
* Resolve custom tools that the SDK should expose directly from session.tools().
*
* @internal
*/
export function getPreloadedCustomToolSlugs(
customToolsMap: CustomToolsMap | undefined,
defaultPreload = false
): string[] {
if (!customToolsMap) {
return [];View on GitHub (pinned to 64b1b85502)
Solutions
- Remove the custom tool slug from preload.tools and set `preload: true` on the custom tool definition
- For a custom toolkit, set preload: true on the toolkit definition instead
- Verify remaining preload.tools entries are ordinary (non-custom) tool slugs
Example fix
// before
await composio.sessions.create({ preload: { tools: ['my_custom_tool'] } });
// after
const tool = defineTool({ slug: 'my_custom_tool', preload: true, /* ... */ });
await composio.sessions.create({}); Defensive patterns
Strategy: validation
Validate before calling
const customSlugs = new Set([...customTools.map(t => t.slug.toUpperCase()), ...customToolsMap?.ambiguousOriginalSlugs ?? []]);
const bad = (preload.tools ?? []).filter(s => customSlugs.has(s.toUpperCase()));
if (bad.length) throw new Error(`Custom slugs in preload.tools: ${bad.join(', ')}`); Type guard
const preloadIsSafe = (preloadTools: string[], customSlugs: Set<string>): boolean => preloadTools.every(s => !customSlugs.has(s.toUpperCase()));
Try / catch
try { await composio.sessions.create(cfg); } catch (e) { if (e instanceof ValidationError && e.message.includes('preload.tools')) { cfg.preload.tools = []; delete cfg.preload; /* set preload on tool instead */ } } Prevention
- Never put custom tool slugs in preload.tools
- Set preload: true on the defineTool call
- Document that preload.tools is backend-tools-only
When it happens
Trigger: Passing a session config whose preload.tools array contains a slug that resolves to an SDK custom tool or custom toolkit tool (checked against the custom tools map, including ambiguous original slugs). Raised from assertNoCustomToolSlugsInPreload during prepareInlineCustomTools.
Common situations: Migrating a config that preloaded a standard tool and swapping in a custom tool slug; assuming preload.tools accepts anything in the tools list; referencing a custom tool by original slug that also exists in the map.
Related errors
- Failed to fetch file from URL: {_sanitize_url_for_logging(ur
- URL returned redirect to {_sanitize_url_for_logging(location
- Custom tool slug collision: "${finalSlug}" is already regist
- Custom tool slug collision: original slug "${handle.slug}" i
- Invalid proxy execute parameters
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/055ccedc6a8a5f70.
Report an issue: GitHub.