different-ai/openwork · error · ToolRuntimeError
InvalidToolInput
InvalidToolInput
Error message
Tool '${name}' expects exactly one input object. What it means
Callable tools in codemode accept exactly one argument: a single input object matching the tool's schema. When the script calls a tool with zero arguments or more than one positional argument, the runtime throws InvalidToolInput before the tool runs. Spread the arguments into one object instead.
Source
Thrown at packages/codemode/src/tool-runtime.ts:773
return {
root: new ToolReference([]),
calls,
keys: (path) => namespaceKeys(callableTools, path),
invoke: (path, args) =>
Effect.gen(function* () {
const name = path.join(".")
const externalArgs = args.map((arg) => copyOut(copyIn(arg, `Arguments for tool '${name}'`)))
const call = { name }
const recordAndObserve = (input: unknown) =>
Effect.sync(() => {
recordCall(call)
return calls.length - 1
}).pipe(Effect.tap((index) => hooks?.onToolCallStart?.({ index, name, input }) ?? Effect.void))
const tool = resolve(callableTools, path)
let describedInput: unknown
if (isDefinition(tool)) {
if (externalArgs.length !== 1)
throw new ToolRuntimeError("InvalidToolInput", `Tool '${name}' expects exactly one input object.`)
describedInput = yield* Effect.try({
try: () => decodeToolInput(tool, externalArgs[0]),
catch: (cause) =>
new ToolRuntimeError("InvalidToolInput", `Invalid input for tool '${name}': ${String(cause)}`),
})
}
const input = isDefinition(tool) ? describedInput : externalArgs
const index = yield* recordAndObserve(input)
const currentCall = { index, name, input }
if (isDefinition(tool)) {
return yield* observeEnd(
Effect.gen(function* () {
const raw = yield* runHost(Effect.suspend(() => tool.run(describedInput)))
const result = yield* Effect.try({
try: () => decodeToolOutput(tool, raw),
catch: () => new ToolRuntimeError("InvalidToolOutput", `Invalid output from tool '${name}'.`),
})
return yield* decodeOutput(result, name)View on GitHub (pinned to 2b7df46e8a)
Solutions
- Wrap all arguments into one object: call the tool as tool({ param1: a, param2: b })
- Check the tool's declared input schema and supply exactly one object whose fields match it
- If arguments are already in an array, pass the object itself, not the spread: tool(argsObj) instead of tool(...args)
- Log externalArgs before the call to confirm only one value is being passed
Example fix
// before
const result = yield* callTool('search', query, limit)
// after
const result = yield* callTool('search', { query, limit }) Defensive patterns
Strategy: validation
Validate before calling
function validateToolCall(args) {
if (!Array.isArray(args) || args.length !== 1 || typeof args[0] !== 'object' || args[0] === null) {
throw new Error('Tool calls must pass exactly one input object: tool({ ... })');
}
} Type guard
function isSingleInputObject(args: unknown[]): args is [Record<string, unknown>] {
return args.length === 1 && typeof args[0] === 'object' && args[0] !== null;
} Try / catch
try {
yield* callTool(name, input)
} catch (e) {
if (e instanceof ToolRuntimeError && e.code === 'InvalidToolInput') {
console.error(`Bad input for ${name}: wrap args in one object`, e.message)
} else throw e
} Prevention
- Always call tools as tool({ field: value }) with a single object
- Read each tool's input schema before wiring calls
- Never spread multiple variables directly as tool arguments
- Lint/generate codemode scripts against the tool schema
When it happens
Trigger: Calling a tool from a codemode script as tool() or tool(a, b, c) — i.e. externalArgs.length !== 1 — instead of tool({...}) with one object argument.
Common situations: Porting scripts written for APIs where tools take individual positional parameters; forgetting the single input object entirely; dynamically forwarding multiple script variables to a tool via apply/spread without wrapping them.
Related errors
- String.${name} expects argument ${index + 1} to be a number.
- String.repeat expects a finite non-negative count.
- Array.from expects an array, string, Map, Set, or array-like
- JSON.parse expects a string.
- Math.${name} expects number arguments.
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/d52af0b3c5b9cb41.
Report an issue: GitHub.