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
- 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.
- Use a stronger model or lower temperature to reduce malformed JSON output.
- Enable the agent's tool-argument repair/retry path (feed the parse error back so the model re-issues the call).
- 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
- Keep tool parameter schemas small and explicitly typed.
- Prefer stronger models for tool-heavy workflows.
- Send parse failures back to the model for automatic self-correction.
- Verify stream collection completes fully before parsing arguments.
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
- agent compaction summary is empty
- agent context cannot be compacted enough
- decode existing session data failed
- decode session data failed
- errContextCannotBeCompacted
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)