mastra-ai/mastra · error · Error
Plugin tool "${toolName}" is no longer available
Error message
Plugin tool "${toolName}" is no longer available What it means
Live plugin tools are proxied: on each execute() the manager reloads changed local plugins and looks the tool up by name in the latest rawPluginTools map. If the tool no longer exists (or lost its execute) after a reload, the proxy throws 'Plugin tool "<name>" is no longer available' instead of silently calling a stale implementation.
Source
Thrown at mastracode/sdk/src/plugins/manager.ts:231
}
}
for (const [name, tool] of Object.entries(nextTools)) {
this.rawPluginTools[name] = tool;
if (!this.pluginTools[name]) {
this.pluginTools[name] = this.createLiveToolProxy(name);
}
this.syncLiveToolProxy(name, tool);
}
}
private createLiveToolProxy(toolName: string) {
return {
execute: async (...args: any[]) => {
await this.reloadChangedLocalPlugins();
const latestTool = this.rawPluginTools[toolName];
if (!latestTool?.execute) {
throw new Error(`Plugin tool "${toolName}" is no longer available`);
}
return (latestTool.execute as (...args: any[]) => unknown)(...args);
},
} as LoadedPlugin['tools'][string];
}
private syncLiveToolProxy(toolName: string, tool: LoadedPlugin['tools'][string]): void {
const proxy = this.pluginTools[toolName];
if (!proxy) return;
const mutableProxy = proxy as unknown as Record<string, unknown>;
for (const key of Object.keys(mutableProxy)) {
delete mutableProxy[key];
}
Object.assign(proxy, tool);
proxy.execute = this.createLiveToolProxy(toolName).execute;
}
private async reloadChangedLocalPlugins(): Promise<void> {View on GitHub (pinned to 75dd419e61)
Solutions
- Re-resolve the tool from the manager after plugin changes instead of caching the old reference.
- Verify the plugin is still installed and enabled and that it still exports the tool under the same name.
- Fix any plugin source errors that make reload drop the tool (check reload logs).
- Restart the session/manager so the tool registry is rebuilt if hot-reload state is inconsistent.
Example fix
// before
const tool = manager.getTool('search'); // cached
await tool.execute(args); // may throw after reload
// after
const fresh = manager.getTool('search');
if (!fresh) throw new Error('tool removed');
await fresh.execute(args); Defensive patterns
Strategy: try-catch
Validate before calling
const tool = manager.getTool?.(toolName) ?? manager.rawPluginTools?.[toolName];
if (!tool || typeof tool.execute !== 'function') {
throw new Error(`Tool "${toolName}" unavailable; refusing to invoke stale proxy`);
} Type guard
function isLiveTool(t: unknown): t is { execute: (...args: any[]) => unknown } {
return typeof t === 'object' && t !== null && typeof (t as any).execute === 'function';
} Try / catch
try {
await pluginTool.execute(args);
} catch (err) {
if (err instanceof Error && /is no longer available/.test(err.message)) {
// re-fetch the tool from the manager and retry once, or surface a 'plugin changed' error
const fresh = manager.getPluginTools()[toolName];
if (fresh) return fresh.execute(args);
throw new Error(`Tool ${toolName} was removed by a plugin reload`);
}
throw err;
} Prevention
- Look up tools fresh from the manager on every use instead of caching proxies across reloads.
- After editing plugin source, re-resolve tool references before running dependent agents/workflows.
- Keep tool names stable across plugin refactors, or update all consumers in the same change.
When it happens
Trigger: Holding a reference to a plugin tool and invoking execute() after (a) the plugin providing the tool was uninstalled/disabled, (b) the plugin renamed or removed the tool, (c) reloadChangedLocalPlugins picked up edited plugin source that no longer exports that tool, or (d) a plugin failed to reload so its tools vanished from rawPluginTools.
Common situations: Hot-reload during development while editing plugin code; an agent/workflow captured the tool reference earlier and runs later after the plugin changed; the plugin file was deleted or moved; a syntax error in the plugin caused a reload that dropped its tools; tool name refactored without updating callers.
Related errors
- Plugin tool "${name}" must be an object with a tool property
- mastra-wrapper plugin did not return code, there is likely a
- @mastra/livekit: the agent requested tool approval or suspen
- MastraFactory: integration tool '${name}' from '${ownerId}'
- Factory rules.tools must be an object.
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/f0eaead6484ea062.
Report an issue: GitHub.