If you've been building agents with the Claude SDK, you may have run into this:
InputValidationError: Tool not found or schema not loaded
The tool name is correct. The code looks fine. But the call keeps failing. I'm Masaki Hirokawa — an independent developer who has shipped apps with over 50 million cumulative downloads, including Beautiful HD Wallpapers for iOS and Android. I build most of my automation tooling on the Claude SDK, and this specific error tripped me up more than once while setting up the content pipeline behind Dolice Labs.
The fix is straightforward once you understand what deferred tools are — but the documentation is easy to skim over without the concept clicking. Here's what's actually happening and how to resolve it.
Why InputValidationError Happens
The Claude SDK — and higher-level implementations like Cowork — uses a deferred tools mechanism. Rather than loading every tool's full JSON Schema into the context window at startup, it defers loading until the tool is actually needed.
When a tool is in the deferred state, the agent knows the tool's name but not its parameter schema (required fields, types, validation constraints). If you call a deferred tool directly, the SDK has no schema to validate your inputs against and throws InputValidationError.
// ❌ This fails when the schema hasn't been loaded
const result = await callTool("WebSearch", { query: "Claude API changelog" });
// → InputValidationError: schema not loaded for 'WebSearch'The reason for this design is practical: tool sets — especially those extended via MCP plugins — vary by session. Loading every schema upfront would bloat the context window. Deferred loading means only the schemas you actually use get loaded.
Fix: Load the Schema with ToolSearch First
The solution is to call ToolSearch before calling the tool. This fetches the full schema and makes the tool callable.
// ✅ Correct order
// Step 1: fetch the schema
await ToolSearch({ query: "select:WebSearch" });
// Step 2: now the tool is safe to call
const result = await WebSearch({ query: "Claude API changelog" });ToolSearch supports two query formats:
Direct selection (most reliable):
await ToolSearch({ query: "select:WebSearch,TaskCreate,TaskUpdate" })Keyword search:
await ToolSearch({ query: "bash shell execute" })When you need multiple tools, use select: with comma-separated names to resolve all of them in a single call. Keyword search is useful when you don't know the exact name, but direct selection is more predictable.
Identifying Deferred Tools
In Cowork (which runs on the Claude Agent SDK), the session system prompt includes a <system-reminder> block that lists all deferred tools at startup:
The following deferred tools are now available via ToolSearch.
Their schemas are NOT loaded — calling them directly will fail with InputValidationError.
Use ToolSearch with query "select:<name>[,<name>...]" to load tool schemas before calling them:
TaskCreate
TaskGet
WebSearch
mcp__workspace__bash
mcp__4cebb999__generate-design
...
If you see this reminder, run ToolSearch before calling any tool on the list.
Step-by-Step Debugging
Here's the exact process I use when an agent starts throwing InputValidationError in one of my scheduled tasks:
Step 1: Read the full error message
InputValidationError: Tool 'mcp__workspace__bash' parameters validation failed
If the error mentions a specific tool name and includes "schema not loaded" or "validation failed," deferred tools are almost certainly the issue. Distinguish this from ToolNotFoundError, which means the tool simply doesn't exist — a different problem with a different fix.
Step 2: Check the system-reminder
Scan the conversation log for the <system-reminder> block and confirm that the tool you're trying to call appears in the deferred list.
Step 3: Load the schema, then retry
// Load the schema first
await ToolSearch({ query: "select:mcp__workspace__bash" });
// Now call the tool
const output = await mcp__workspace__bash({ command: "ls /tmp" });In the vast majority of cases, this resolves the issue immediately.
Common Mistake Patterns
Pattern 1: Assuming schemas persist across sessions
A deferred tool that worked in a previous session returns to the deferred state when a new session starts. Scheduled tasks that spin up a fresh session each run are the most common place this bites you.
// ❌ Worked yesterday, failing now — because it's a new session
const result = await mcp__workspace__bash({ command: "echo hello" });// ✅ Load at session start, every session
await ToolSearch({ query: "select:mcp__workspace__bash,TaskCreate,TaskUpdate" });
// Safe to call from here onPattern 2: Calling a newly-installed plugin immediately
Newly installed MCP servers and plugins start in the deferred state. The most common version of this: you install the Canva or Figma plugin, try to call one of its tools right away, and get InputValidationError. The fix is always the same — run ToolSearch first.
Pattern 3: Not awaiting ToolSearch
// ❌ Fire and forget — schema may not be ready on the next line
ToolSearch({ query: "select:WebSearch" });
WebSearch({ query: "..." }); // Schema may still be deferred// ✅ Always await ToolSearch before calling the tool
await ToolSearch({ query: "select:WebSearch" });
const result = await WebSearch({ query: "..." });Pattern 4: Changing parameters instead of loading the schema
InputValidationError sounds like a problem with your input values, which leads some developers to iterate on parameters rather than fixing the schema load. If the error message includes "schema not loaded" rather than specific field validation failures, the parameters themselves are not the issue.
Production Pattern: Load at Session Start
My automated publishing system generates daily articles across four sites. Each scheduled task runs in a new session, so I load all required tool schemas as the very first step of every task.
// Always the first thing a scheduled task does
async function initializeTools() {
await ToolSearch({
query: "select:mcp__workspace__bash,TaskCreate,TaskUpdate,WebSearch"
});
}
async function runDailyTask() {
await initializeTools();
// All tools are now safe to call
const wsResult = await mcp__workspace__bash({
command: 'ls -d /sessions/*/mnt/Workspace 2>/dev/null | head -1'
});
const ws = wsResult.stdout.trim();
// ... rest of task
}Since adopting this pattern, I haven't had a single task fail due to deferred tool errors. The ToolSearch call itself takes only a few hundred milliseconds — negligible against the total task runtime.
MCP Plugin Tools
Tools from MCP plugins like Canva, Figma, or Slack follow a naming pattern of mcp__{uuid}__{tool-name}, which makes them harder to reference directly.
// Full name approach
await ToolSearch({
query: "select:mcp__4cebb999-208a-460c-96cf-29ee38abf5d6__generate-design"
});When the UUID feels unwieldy, keyword search works too:
// Keyword approach
await ToolSearch({ query: "canva generate design" });Looking back
When a tool call throws InputValidationError, check for deferred tools before assuming anything is wrong with your inputs:
- Check the
<system-reminder>deferred tools list for your tool's name - Call
await ToolSearch({ query: "select:ToolName" })to load the schema - Confirm the await completes before calling the tool
For scheduled tasks and automation pipelines that start a new session each run, load all schemas upfront at session start. It's a small, reliable pattern that eliminates this entire class of errors.