openclaw/openclaw · error

Codex settled-turn projection requires object tool arguments

Error message

Codex settled-turn projection requires object tool arguments

What it means

Thrown by serializeToolArguments when tool arguments were supplied as a string, JSON.parse succeeded, but the parsed value is not a record object (e.g. an array, number, string, boolean, or null). Codex function_call arguments must serialize to a JSON object, so a JSON string that parses to a non-object is rejected even though it is valid JSON.

Source

Thrown at extensions/codex/src/app-server/settled-turn-projection.ts:76

function requireToolName(value: unknown): string {
  const name = readNonEmptyString(value);
  if (!name || !TOOL_NAME_PATTERN.test(name)) {
    throw new Error("Codex settled-turn projection found an invalid tool name");
  }
  return name;
}

function serializeToolArguments(value: unknown): string {
  if (typeof value === "string") {
    let parsed: unknown;
    try {
      parsed = JSON.parse(value);
    } catch {
      throw new Error("Codex settled-turn projection found invalid JSON tool arguments");
    }
    if (!isRecord(parsed)) {
      throw new Error("Codex settled-turn projection requires object tool arguments");
    }
    return requireBoundedText(value, "tool arguments");
  }
  if (!isRecord(value)) {
    throw new Error("Codex settled-turn projection requires object tool arguments");
  }
  let serialized: string;
  try {
    serialized = JSON.stringify(value);
  } catch {
    throw new Error("Codex settled-turn projection found unserializable tool arguments");
  }
  return requireBoundedText(serialized, "tool arguments");
}

function projectUserMessage(message: AgentMessage): JsonValue[] {
  const record = message as unknown as Record<string, unknown>;
  const upstreamUserText = readUpstreamUserText(message);

View on GitHub (pinned to 01804a7531)

Solutions

  1. Inspect the parsed value of arguments to see its actual type.
  2. Ensure the toolCall producer builds arguments as a plain object {} and JSON.stringify it.
  3. If the upstream legitimately uses positional args, wrap them as { args: [...] } at the adapter.

Example fix

// before
{ type: "toolCall", id, name, arguments: JSON.stringify([1, 2, 3]) } // parses to array -> throws

// after
{ type: "toolCall", id, name, arguments: JSON.stringify({ args: [1, 2, 3] }) }
Defensive patterns

Strategy: validation

Validate before calling

function isJsonObjectArgumentsString(value: unknown): boolean {
  if (typeof value !== "string") return false;
  try {
    const parsed = JSON.parse(value);
    return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed);
  } catch {
    return false;
  }
}

// at the producer
if (typeof args === "string") {
  const parsed = JSON.parse(args);
  if (Array.isArray(parsed) || typeof parsed !== "object" || parsed === null) {
    args = JSON.stringify({ value: parsed });
  }
}

Type guard

function parsesToJsonObject(value: unknown): boolean {
  if (typeof value !== "string") return false;
  try {
    const p = JSON.parse(value);
    return typeof p === "object" && p !== null && !Array.isArray(p);
  } catch {
    return false;
  }
}

Prevention

When it happens

Trigger: arguments is the string "[1,2,3]" (parses to array), "42" (number), "\"text\"" (string), "null", or "true"; a provider that stringified a non-object value as arguments.

Common situations: Provider adapter that stringified a positional-arguments array instead of a named-args object; a model that returned a bare scalar as arguments; test fixture using JSON.stringify on a non-object.

Related errors


AI-assisted analysis of openclaw/openclaw@01804a7531 (2026-08-12). Data as JSON: /api/errors/6364abadc350e213. Report an issue: GitHub.