siyuan-note/siyuan · error
capability handler unavailable: %s
Error message
capability handler unavailable: %s
What it means
The last validation in validateCapabilityCall (kernel/agent/tools.go:72-75): a non-browser capability must have a Tool whose ContextHandler or Handler is set. Browser capabilities (Runtime == "browser") are exempt because their handlers live in the frontend and calls are dispatched back over SSE. Failing this check means the registration points at a backend tool with no executable function — an internal registration bug, not a user input problem.
Source
Thrown at kernel/agent/tools.go:74
func validateCapabilityCall(ctx context.Context, registration *capabilityRegistration, args map[string]any) error {
if registration == nil {
return fmt.Errorf("capability was not exposed in this model round")
}
if !capabilityStillExecutable(registration, args) {
return fmt.Errorf("capability is disabled or no longer available: %s", registration.ID)
}
if ctx.Err() != nil {
return fmt.Errorf("capability execution was cancelled before it started")
}
if registration.Validator == nil {
return fmt.Errorf("capability validator unavailable: %s", registration.ID)
}
if err := registration.Validator.ValidateInputContext(ctx, args); err != nil {
return fmt.Errorf("invalid capability arguments: %w", err)
}
if !registration.isBrowser() &&
(registration.Tool == nil || registration.Tool.ContextHandler == nil && registration.Tool.Handler == nil) {
return fmt.Errorf("capability handler unavailable: %s", registration.ID)
}
return nil
}
// executeTool 执行单次工具调用。
func executeTool(ctx context.Context, tc openai.ToolCall, sessionID string) executedToolResult {
tool, validator := tools.LookupToolWithValidator(tc.Function.Name)
if tool == nil {
return executedToolResult{Text: "unknown tool: " + tc.Function.Name, IsError: true}
}
return executeCapability(ctx, tc, sessionID, &capabilityRegistration{
ID: tools.CapabilityIDForTool(tool),
ModelName: tool.Name,
Source: tool.Source,
Runtime: tool.Runtime,
Tool: tool,
Validator: validator,
})View on GitHub (pinned to afa823b6b4)
Solutions
- Look up the capability ID in the message, find its registration, and set Tool.ContextHandler or Tool.Handler
- If the capability is frontend-hosted, ensure Runtime is exactly "browser" (isBrowser check, capability.go:109-111) so it takes the SSE dispatch path
- Assert in tests that every registered non-browser tool has a handler (pattern in kernel/agent/capability_test.go)
- If hit with unmodified stock tools, report the capability ID — it marks a packaging/version defect
Defensive patterns
Strategy: try-catch
Validate before calling
// Registration builders: require a handler for backend tools
if !reg.isBrowser() && (reg.Tool == nil || (reg.Tool.ContextHandler == nil && reg.Tool.Handler == nil)) {
// refuse registration
} Type guard
null
Try / catch
if err := validateCapabilityCall(ctx, reg, args); err != nil {
if strings.Contains(err.Error(), "capability handler unavailable") {
// internal defect: log capability ID and drop the registration
}
} Prevention
- Set Runtime "browser" only for frontend-hosted capabilities (SSE dispatch), never to mask a missing backend handler
- Test that every registered non-browser tool executes in CI
When it happens
Trigger: A backend capabilityRegistration built with Tool == nil or with a tools.Tool that defines neither Handler nor ContextHandler — e.g. a tool registered for its schema/effects only, a refactor removing a handler without pruning the registration, or a native tool declaration misclassified as non-browser. Browser-capability paths (handleBrowserCapability) never hit it.
Common situations: Kernel/plugin development: declaring a tool's schema for prompt-building but wiring no handler; copy-paste registrations; version skew where a tool struct lost its handler in a refactor.
Understand the failure class
Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.
Related errors
- capability validator unavailable: %s
- capability was not exposed in this model round
- capability is disabled or no longer available: %s
- capability execution was cancelled before it started
- invalid capability arguments: %w
AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18).
Data as JSON: /api/errors/ff5f057428a4ae12.
Report an issue: GitHub.