siyuan-note/siyuan · warning

invalid capability arguments

Error message

invalid capability arguments: %w

What it means

Wraps a JSON-parsing failure of a tool call's arguments into a toolInputErr with the message "invalid capability arguments". When the model returns a tool/capability call whose Arguments string cannot be parsed (parseErrors[i] != nil), the agent labels it and skips validation/execution, instead reporting the invalid arguments back to the model as a failed tool call.

Solutions

  1. Inspect the wrapped %w error to see the exact JSON parse failure position and fix the tool's parameter schema/description so the model emits valid JSON.
  2. Use a stronger model or lower temperature to reduce malformed JSON output.
  3. Enable the agent's tool-argument repair/retry path (feed the parse error back so the model re-issues the call).
  4. Check the provider/stream handling for truncation — if arguments are cut off at the stream end, verify stream collection completes before parsing.

Example fix

// before
toolInputErr := parseErrors[i]
if toolInputErr != nil {
    toolInputErr = fmt.Errorf("invalid capability arguments: %w", toolInputErr)
}
// after: attempt a lenient repair pass before reporting failure
args, parseErr := parseToolArguments(tc.Arguments)
if parseErr != nil {
    args, parseErr = repairToolArguments(tc.Arguments)
}
toolInputErr = parseErr
Defensive patterns

Strategy: validation

Validate before calling

function hasValidToolArgs(call) {
  try { JSON.parse(call.arguments); return true } catch { return false }
}

Type guard

func isValidJSONObject(s string) bool {
    var v any
    return json.Unmarshal([]byte(s), &v) == nil
}

Try / catch

args, err := parseToolArguments(tc.Arguments)
if err != nil {
    // feed the "invalid capability arguments" error back to the model so it re-issues the call
    appendToolErrorMessage(tc.ID, err)
}

Prevention

When it happens

Trigger: In agent.go (~line 1214-1216): the model emitted a tool call whose tc.Arguments JSON is malformed (syntax error, truncated JSON, wrong types), so the per-call parse produced parseErrors[i]; the raw parse error is then wrapped as fmt.Errorf("invalid capability arguments: %w", toolInputErr).

Common situations: A weak or quantized model emits malformed JSON arguments (unescaped newlines/quotes, trailing commas); the model stream was truncated mid-arguments; a model emits arguments as plain text instead of JSON; tool argument schemas are large and the model rambles.

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@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/773f5bb9f099198e. Report an issue: GitHub.

Appendix: source

Thrown at kernel/agent/agent.go:1216

				for i, tc := range aggregatedToolCalls {
					args := parsedArgs[i]
					registration := roundCapabilities.registration(tc.Function.Name)
					action := ""
					if a, ok := args["action"]; ok {
						action, _ = a.(string)
					}

					sendEvent(ch, AgentEvent{
						Type:       "tool_call",
						Name:       tc.Function.Name,
						RoundID:    roundID,
						ToolCallID: tc.ID,
						Arguments:  args,
					})

					toolInputErr := parseErrors[i]
					if toolInputErr != nil {
						toolInputErr = fmt.Errorf("invalid capability arguments: %w", toolInputErr)
					} else {
						toolInputErr = validateCapabilityCall(ctx, registration, args)
					}
					requiresConfirm, forcedConfirm := false, false
					if toolInputErr == nil {
						requiresConfirm, forcedConfirm = capabilityConfirmRequirement(
							registration, action, args, permissionController.allowSession.Load(), nil)
					}
					if requiresConfirm {
						confirmID := fmt.Sprintf("%s_%s_%d", turn.TurnID, tc.ID, i)
						ch2 := make(chan confirmResult, 1)
						confirmChannelsMu.Lock()
						confirmChannels[confirmID] = &confirmWaiter{sessionID: sessionID, ch: ch2}
						confirmChannelsMu.Unlock()
						effects := capabilityEffects(registration, action)
						sendCriticalEvent(ctx, ch, AgentEvent{
							Type: "confirm", Name: tc.Function.Name, Arguments: args, ConfirmID: confirmID, Effects: effects,
							ForcedConfirm: forcedConfirm, CapabilityID: registration.ID,

View on GitHub (pinned to 9f775e8a12)