mastra-ai/mastra · error · HTTPException
Tool is not executable
Error message
Tool is not executable
What it means
A 400 thrown when the resolved tool exists but has no execute function — the tool object lacks the executable capability required by the execution endpoint. Typically indicates the ID resolved to a non-executable artifact or a malformed tool definition.
Source
Thrown at packages/server/src/server/handlers/tools.ts:265
if (!tool) {
try {
tool = mastra.getToolById(toolId);
} catch {
// tool not found in global registry, continue to agent fallback
}
}
// Fallback: search dynamically-resolved agent tools (toolsResolver)
if (!tool) {
tool = await findToolInAgents(mastra, toolId, requestContext);
}
if (!tool) {
throw new HTTPException(404, { message: 'Tool not found' });
}
if (!tool?.execute) {
throw new HTTPException(400, { message: 'Tool is not executable' });
}
const { data } = bodyParams;
validateBody({ data });
let result;
if (isVercelTool(tool)) {
result = await (tool as any).execute(data);
} else {
result = await tool.execute(data!, {
mastra,
requestContext,
// TODO: Pass proper tracing context when server API supports tracing
tracingContext: { currentSpan: undefined },
...(runId
? {
workflow: {View on GitHub (pinned to 75dd419e61)
Solutions
- Add an execute function to the tool definition (via createTool) so it is runnable server-side.
- Verify you registered/exported the actual tool object, not a metadata fragment.
- Pick a different, executable tool if this one is intentionally definition-only.
Example fix
// before
export const weatherTool = createTool({ id: 'weather', inputSchema, outputSchema }); // no execute
// after
export const weatherTool = createTool({
id: 'weather',
inputSchema,
outputSchema,
execute: async ({ context }) => fetchWeather(context),
}); Defensive patterns
Strategy: type-guard
Validate before calling
const tool = await api.get(`/api/tools/${toolId}`);
if (!tool || typeof tool.execute !== 'function') {
throw new Error(`Tool ${toolId} exists but has no execute implementation`);
} Type guard
function isExecutableTool(tool: unknown): tool is { id: string; execute: (args: never) => Promise<unknown> } {
return !!tool && typeof (tool as any).execute === 'function';
} Try / catch
try {
return await api.post(`/api/tools/${toolId}/execute`, { data });
} catch (e) {
if (e.status === 400 && /not executable/i.test(e.message)) return null;
throw e;
} Prevention
- Always define execute when creating tools with createTool.
- Add a build-time lint/test asserting every exported tool has execute.
- Verify you export the tool object itself, not metadata.
When it happens
Trigger: POST execute where the resolved tool (from registeredTools, mastra, or agents toolsResolver) has tool?.execute undefined — e.g. a tool created with createTool missing an execute handler, or a descriptor/placeholder object rather than a real tool.
Common situations: Tools defined only as schemas/specs (e.g. for client-side generation) without an execute implementation; exporting the wrong symbol (metadata instead of tool); partial upgrade where tool shape changed.
Related errors
- Tool ID is required
- bad request: ${responseText}
- Factory rules.tools must be an object.
- Factory rules.tools.${toolName} must be an object.
- Factory rules.tools.${toolName}.onResult must be a function.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/dcf0ae0a49f4a375.
Report an issue: GitHub.