JuliusBrussee/caveman · error
cave_delegate_missing_task
Error message
cave_delegate_missing_task
What it means
The caveman-delegate MCP server throws this when a tools/call arrives without a usable arguments.task — the task must be a non-empty string; it is the one required argument of the delegate tool and the prompt handed to the worker subprocess.
Source
Thrown at agents/delegate/caveman-delegate-mcp.mjs:143
},
};
async function handle(msg) {
const { id, method, params } = msg;
if (method === "initialize") {
return {
protocolVersion: params?.protocolVersion || PROTOCOL_FALLBACK,
capabilities: { tools: { listChanged: false } },
serverInfo: { name: "caveman-delegate", version: "0.1.0" },
};
}
if (method === "tools/list") return { tools: [TOOL] };
if (method === "ping") return {};
if (method === "tools/call") {
if (params?.name !== TOOL.name) throw new Error(`unknown tool: ${params?.name}`);
if (process.env.CAVEMAN_DELEGATE_CHILD === "1") throw new Error("cave_delegate_depth_exceeded");
const task = params?.arguments?.task;
if (!task || typeof task !== "string") throw new Error("cave_delegate_missing_task");
const r = await runWorker(task, params?.arguments?.cwd);
const lines = [r.out || "(worker produced no output)"];
if (r.usage) {
const u = r.usage;
let usageLine = `delegate usage (measured, provider-reported): input ${u.input + u.cacheRead + u.cacheWrite} (cacheRead ${u.cacheRead}, cacheWrite ${u.cacheWrite}), output ${u.output}`;
if (u.cost > 0) usageLine += `, cost $${u.cost.toFixed(4)}`;
lines.push("---", usageLine);
}
if (r.code !== 0) {
lines.push("---", `worker exit ${r.code}: ${r.err.slice(-500)}`);
return { content: [{ type: "text", text: lines.join("\n") }], isError: true };
}
return { content: [{ type: "text", text: lines.join("\n") }] };
}
if (method?.startsWith("notifications/")) return undefined;
throw Object.assign(new Error(`method not found: ${method}`), { code: -32601 });
}
View on GitHub (pinned to 27d5a3981a)
Solutions
- Pass arguments as { task: "<non-empty instruction string>" }
- Validate and require the task field in the caller's form or schema before issuing tools/call
- If the task is built from variables, fail loudly upstream when it composes to empty
Example fix
// before
await rpc("tools/call", { name, arguments: { cwd } }); // task missing
// after
if (!task?.trim()) throw new Error("task is required");
await rpc("tools/call", { name, arguments: { task, cwd } }); Defensive patterns
Strategy: validation
Validate before calling
const callArgs = { task, cwd };
if (typeof callArgs.task !== "string" || callArgs.task.trim() === "") {
throw new Error("delegate requires a non-empty task string");
}
await rpc("tools/call", { name, arguments: callArgs }); Type guard
function isDelegateArgs(a) {
return typeof a === "object" && a !== null && typeof a.task === "string" && a.task !== "";
} Try / catch
try { await callDelegate(task); }
catch (e) {
if (String(e?.message) === "cave_delegate_missing_task") { /* surface a form-level 'task required' error */ }
else throw e;
} Prevention
- Require the task field in the client's input schema or UI before the call
- Compose task strings from variables only after checking they are non-empty
When it happens
Trigger: Calling the delegate tool with arguments omitted, with task set to null/undefined/number/object, or with an empty string — commonly a client schema bug or a template variable that rendered empty.
Common situations: Client forms that submit before input; templating producing empty strings; passing the task under a different key (prompt, query, description); JSON.stringify dropping undefined fields.
Related errors
- ${key} is required
- filters must be an object
- unknown trace filter(s): ${unknown.sort().join(", ")}
- filter ${key} must be a non-empty array of non-empty strings
- filter status_class values must be 2xx, 4xx, or 5xx
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/e5d4448018267bc2.
Report an issue: GitHub.