siyuan-note/siyuan · error

tool arguments must be a JSON object

Error message

tool arguments must be a JSON object

What it means

After a successful json.Unmarshal, parseToolArgs checks that the result is a non-nil map. JSON text like `null` or `"string"` unmarshals without error into a nil map (or fails type conversion), so this error rejects arguments that are valid JSON but not a JSON object. Tool arguments must be a key/value object for the capability executor to look up parameters.

Source

Thrown at kernel/agent/tools.go:345

		if data, err := json.Marshal(result.StructuredContent); err == nil {
			return string(data)
		}
	}
	return "(empty result)"
}

// parseToolArgs 在流结束后解析完整的工具参数,避免把损坏的 JSON 误报为缺少 schema 字段。
func parseToolArgs(argsJSON string) (map[string]any, error) {
	if strings.TrimSpace(argsJSON) == "" {
		return map[string]any{}, nil
	}

	var args map[string]any
	if err := json.Unmarshal([]byte(argsJSON), &args); err != nil {
		return nil, fmt.Errorf("tool arguments are not valid JSON: %w", err)
	}
	if args == nil {
		return nil, fmt.Errorf("tool arguments must be a JSON object")
	}
	return args, nil
}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Ensure the tool-call arguments field is serialized as a JSON object `{...}`, even when empty: `{}` instead of `null`.
  2. For no-parameter tools, have callers pass `{}` explicitly; parseToolArgs also returns an empty map for empty strings.
  3. Sanitize upstream input: if argsJSON == "null" (after trim), replace with "{}" before parsing if the tool tolerates empty args.

Example fix

// before
parseToolArgs("null") // error: must be a JSON object

// after
if strings.TrimSpace(argsJSON) == "null" {
    argsJSON = "{}"
}
parsed, err := parseToolArgs(argsJSON)
Defensive patterns

Strategy: validation

Validate before calling

function ensureObjectArgs(s) {
  const t = (s ?? "").trim();
  if (t === "" || t === "null") return "{}";
  const v = JSON.parse(t);
  if (typeof v !== "object" || v === null || Array.isArray(v)) throw new Error("tool arguments must be a JSON object");
  return t;
}

Type guard

func isJSONObjectArgs(raw string) bool {
    var m map[string]any
    return json.Unmarshal([]byte(raw), &m) == nil && m != nil
}

Try / catch

args, err := parseToolArgs(raw)
if err != nil {
    if err.Error() == "tool arguments must be a JSON object" {
        args = map[string]any{} // degrade to empty args if the tool has no required params
    }
}

Prevention

When it happens

Trigger: Calling parseToolArgs with argsJSON equal to `null`, or any JSON scalar/array that unmarshals into a nil map[string]any (e.g. arrays actually fail unmarshal into map, but `null` succeeds with nil). The classic trigger is a model emitting `null` as the arguments field.

Common situations: An LLM fills in `null` for arguments when a tool takes no parameters; a caller passes an already-decoded nil map serialized back to string; a test fixture uses `null` as arguments.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/58407e760b0d1210. Report an issue: GitHub.