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

  1. Wrap all arguments into one object: call the tool as tool({ param1: a, param2: b })
  2. Check the tool's declared input schema and supply exactly one object whose fields match it
  3. If arguments are already in an array, pass the object itself, not the spread: tool(argsObj) instead of tool(...args)
  4. 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

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


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/d52af0b3c5b9cb41. Report an issue: GitHub.