siyuan-note/siyuan · error

tool arguments are not valid JSON: %w

Error message

tool arguments are not valid JSON: %w

What it means

parseToolArgs in kernel/agent/tools.go wraps a json.Unmarshal failure when converting an LLM tool-call arguments string into a map[string]any. The agent passes whatever JSON text the model produced for tool arguments, and this error means that text is not syntactically valid JSON at all. The underlying encoding/json error is preserved via %w so callers can inspect the exact parse failure.

Source

Thrown at kernel/agent/tools.go:342

		return joined
	}
	if result.HasStructuredContent() {
		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. Log the raw argsJSON string alongside the wrapped error and fix the source that produced the malformed JSON (usually the model prompt or the code assembling the arguments).
  2. If the model regularly emits slightly-off JSON, add a repair/normalization pass (e.g. strip trailing commas, ensure quoting) or instruct the model more strictly to output strict JSON.
  3. In code building arguments programmatically, marshal with json.Marshal instead of string concatenation so the payload is always valid JSON.
  4. Check that streaming assembly of the arguments field is complete before calling parseToolArgs (wait for the finish event).

Example fix

// before
parseToolArgs(`{"path": "/tmp/x",}`) // trailing comma -> error

// after
args, err := json.Marshal(map[string]any{"path": "/tmp/x"})
parsed, err := parseToolArgs(string(args)) // always valid JSON
Defensive patterns

Strategy: validation

Validate before calling

function isValidJSONArgs(s) {
  const t = (s ?? "").trim();
  if (t === "") return true; // parsed as empty args
  if (t === "null") return false;
  try { const v = JSON.parse(t); return v !== null && typeof v === "object" && !Array.isArray(v); } catch { return false; }
}

Type guard

function isJSONObject(v) { return v !== null && typeof v === "object" && !Array.isArray(v); }

Try / catch

args, err := parseToolArgs(raw)
if err != nil {
    var jsonErr *json.SyntaxError
    if errors.As(err, &jsonErr) {
        log.Warnf("bad tool args at offset %d: %s", jsonErr.Offset, raw)
    }
    return fmt.Errorf("retry with strict JSON: %w", err)
}

Prevention

When it happens

Trigger: Calling parseToolArgs (directly or via executeCapability) with an argsJSON string that json.Unmarshal rejects: truncated output, single quotes instead of double quotes, unescaped newlines/quotes inside strings, trailing commas, or non-JSON text such as plain prose produced by the model in the arguments field. An empty/whitespace-only string is fine (returns empty map), so only malformed non-empty input triggers this.

Common situations: An LLM emits tool arguments with comments or JavaScript object syntax instead of JSON; streaming output is cut off mid-object; a prompt template interpolates unescaped quotes into the arguments field; integration tests feed hand-written malformed JSON.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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