siyuan-note/siyuan · error

invalid tool arguments: %w

Error message

invalid tool arguments: %w

What it means

The JSON payload sent as a tool's `arguments` could not be unmarshalled into `map[string]any`. This is the raw transport-level parse failure, distinct from the schema validation that runs next (`ValidateInputContext`) — it means the bytes themselves are not valid JSON or do not decode into a JSON object.

Source

Thrown at kernel/mcp/server.go:265

		Title:       tool.Title,
		Description: tool.Description,
		InputSchema: tool.InputSchema,
	}
	if tool.OutputSchema != nil {
		sdkTool.OutputSchema = tool.OutputSchema
	}
	if tool.ReadOnlyHint {
		sdkTool.Annotations = &mcpsdk.ToolAnnotations{ReadOnlyHint: true}
	}

	server.AddTool(sdkTool, func(ctx context.Context, request *mcpsdk.CallToolRequest) (*mcpsdk.CallToolResult, error) {
		if allowed != nil && !allowed() {
			return toolErrorResult("MCP capability is disabled or no longer available"), nil
		}
		arguments := map[string]any{}
		if len(request.Params.Arguments) > 0 {
			if err := json.Unmarshal(request.Params.Arguments, &arguments); err != nil {
				return nil, fmt.Errorf("invalid tool arguments: %w", err)
			}
		}
		if arguments == nil {
			arguments = map[string]any{}
		}
		if err := validator.ValidateInputContext(ctx, arguments); err != nil {
			return toolErrorResult(fmt.Sprintf("invalid tool arguments: %v", err)), nil
		}
		releaseBoxLeases := func() {}
		if tool.BoxLeaseResolver != nil {
			leaseContext, contextErr := requestOperationContext(ctx, request)
			if contextErr != nil {
				logging.LogWarnf("mcp: acquire request operation scope for tool [%s] failed: %v", name, contextErr)
				return toolErrorResult(contextErr.Error()), nil
			}
			releaseBoxLeases, err = model.AcquireEncryptedBoxOperations(leaseContext, tool.BoxLeaseResolver(arguments))
			if err != nil {
				if errors.Is(err, model.ErrEncryptedBoxNotUnlocked) {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Ensure the `arguments` field is a JSON object (`{...}`), not an array or scalar.
  2. Validate the request JSON with a linter/validator before sending; fix any syntax errors flagged.
  3. If arguments arrive pre-stringified, decode them once on the client before transmission.

Example fix

// before (arguments sent as array)
{"method":"tools/call","params":{"name":"block_move","arguments":["20240101000000-abc"]}}
// after (object keyed by parameter name)
{"method":"tools/call","params":{"name":"block_move","arguments":{"id":"20240101000000-abc","parentID":"20240101000000-def"}}}
Defensive patterns

Strategy: validation

Validate before calling

// Client-side: ensure arguments is a JSON object before sending.
function buildCall(name, args) {
  if (args == null || typeof args !== "object" || Array.isArray(args)) {
    throw new Error("arguments must be a JSON object")
  }
  return JSON.stringify({ method: "tools/call", params: { name, arguments: args } })
}

Type guard

// Narrow arguments to a record before invoking the tool.
function isArgumentsObject(v): v is Record<string, unknown> {
  return v != null && typeof v === "object" && !Array.isArray(v)
}

Prevention

When it happens

Trigger: An MCP `tools/call` request whose `params.arguments` is a JSON array, a bare string/number, or syntactically broken JSON (trailing comma, unescaped quote). The SDK handler at `server.go:264` runs `json.Unmarshal(request.Params.Arguments, &arguments)` and it returns an error.

Common situations: The MCP client sends arguments as an array instead of an object. A hand-crafted JSON-RPC request with malformed JSON. A serialization mismatch where the client encodes arguments twice (double-escaped JSON string).

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/df7d54d762e359db. Report an issue: GitHub.