{"record":{"id":"94d5f8aa7de0259c","repo":"siyuan-note/siyuan","slug":"capability-was-not-exposed-in-this-model-round","errorCode":null,"errorMessage":"capability was not exposed in this model round","messagePattern":"capability was not exposed in this model round","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/agent/tools.go","lineNumber":58,"sourceCode":"\tt, validator := tools.LookupToolWithValidator(toolName)\n\tif t == nil {\n\t\treturn nil, nil, fmt.Errorf(\"unknown tool: %s\", toolName)\n\t}\n\tif t.ContextHandler == nil && t.Handler == nil {\n\t\treturn nil, nil, fmt.Errorf(\"tool handler unavailable: %s\", toolName)\n\t}\n\tif ctx.Err() != nil {\n\t\treturn nil, nil, fmt.Errorf(\"tool execution was cancelled before it started\")\n\t}\n\tif err := validator.ValidateInputContext(ctx, args); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"invalid tool arguments: %w\", err)\n\t}\n\treturn t, validator, nil\n}\n\nfunc validateCapabilityCall(ctx context.Context, registration *capabilityRegistration, args map[string]any) error {\n\tif registration == nil {\n\t\treturn fmt.Errorf(\"capability was not exposed in this model round\")\n\t}\n\tif !capabilityStillExecutable(registration, args) {\n\t\treturn fmt.Errorf(\"capability is disabled or no longer available: %s\", registration.ID)\n\t}\n\tif ctx.Err() != nil {\n\t\treturn fmt.Errorf(\"capability execution was cancelled before it started\")\n\t}\n\tif registration.Validator == nil {\n\t\treturn fmt.Errorf(\"capability validator unavailable: %s\", registration.ID)\n\t}\n\tif err := registration.Validator.ValidateInputContext(ctx, args); err != nil {\n\t\treturn fmt.Errorf(\"invalid capability arguments: %w\", err)\n\t}\n\tif !registration.isBrowser() &&\n\t\t(registration.Tool == nil || registration.Tool.ContextHandler == nil && registration.Tool.Handler == nil) {\n\t\treturn fmt.Errorf(\"capability handler unavailable: %s\", registration.ID)\n\t}\n\treturn nil","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/agent/tools.go#L40-L76","documentation":"Every model round, the agent builds a capabilitySet — the exact tool list exposed to the model for that turn. validateCapabilityCall (kernel/agent/tools.go:57-59) receives the registration looked up by the model-visible tool name; nil means that name was never exposed in this round, so the kernel refuses to execute it. The error text is returned to the model as the tool result (IsError), steering it to use only advertised tools.","triggerScenarios":"The model emits a tool call whose function name is not in the current round's registrations map (capabilitySet.registration returns nil): hallucinated tool names, a tool enabled/configured after the round started, an MCP tool from a server that connected mid-turn, or a frontend browser capability whose generation no longer matches. Reached via the agent chat flow (/api/ai/agent/chat) and browser capability dispatch.","commonSituations":"Plugins or MCP servers enabled while a turn is streaming; model inventing plausible names ('search_web' when only 'web_search' exists); long sessions spanning a reconfiguration; custom tool names colliding after a rename.","solutions":["Surface the error text to the model as-is — it instructs the model that the tool was not exposed; the next round includes the updated tool list","Start a new turn (or new session) after changing enabled plugins/MCP servers so the capability set is rebuilt","Keep tool names stable across plugin versions; avoid renames without a migration","For custom integrations, verify the name against the tools advertised in the current round before dispatching"],"exampleFix":"// model called a tool not in this round's set\n// -> respond with the registration check result so the model self-corrects\nreg := caps.registration(tc.Function.Name);\nif reg == nil {\n    return executedToolResult{Text: \"capability was not exposed in this model round\", IsError: true};\n}","handlingStrategy":"fallback","validationCode":"// Before dispatching a tool call, confirm the name is in this round's advertised tools\nconst exposed = new Set(roundToolList.map(t => t.function.name));\nif (!exposed.has(tc.function.name)) { /* feed correction hint to model, do not execute */ }","typeGuard":"const isExposedTool = (name: string, roundTools: {function: {name: string}}[]) =>\n  roundTools.some(t => t.function.name === name);","tryCatchPattern":"null","preventionTips":["Treat 'capability was not exposed in this model round' as model feedback, not a crash — return it to the model","Restart the turn after changing tool/plugin/MCP configuration","Keep tool names stable across releases; add new tools instead of renaming"],"tags":["agent","capability","tool-registry","model-round"],"backgroundTag":"stale-tool-reference","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}