siyuan-note/siyuan · warning

capability was not exposed in this model round

Error message

capability was not exposed in this model round

What it means

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.

Source

Thrown at kernel/agent/tools.go:58

	t, validator := tools.LookupToolWithValidator(toolName)
	if t == nil {
		return nil, nil, fmt.Errorf("unknown tool: %s", toolName)
	}
	if t.ContextHandler == nil && t.Handler == nil {
		return nil, nil, fmt.Errorf("tool handler unavailable: %s", toolName)
	}
	if ctx.Err() != nil {
		return nil, nil, fmt.Errorf("tool execution was cancelled before it started")
	}
	if err := validator.ValidateInputContext(ctx, args); err != nil {
		return nil, nil, fmt.Errorf("invalid tool arguments: %w", err)
	}
	return t, validator, nil
}

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

View on GitHub (pinned to afa823b6b4)

Solutions

  1. 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
  2. Start a new turn (or new session) after changing enabled plugins/MCP servers so the capability set is rebuilt
  3. Keep tool names stable across plugin versions; avoid renames without a migration
  4. For custom integrations, verify the name against the tools advertised in the current round before dispatching

Example fix

// model called a tool not in this round's set
// -> respond with the registration check result so the model self-corrects
reg := caps.registration(tc.Function.Name);
if reg == nil {
    return executedToolResult{Text: "capability was not exposed in this model round", IsError: true};
}
Defensive patterns

Strategy: fallback

Validate before calling

// Before dispatching a tool call, confirm the name is in this round's advertised tools
const exposed = new Set(roundToolList.map(t => t.function.name));
if (!exposed.has(tc.function.name)) { /* feed correction hint to model, do not execute */ }

Type guard

const isExposedTool = (name: string, roundTools: {function: {name: string}}[]) =>
  roundTools.some(t => t.function.name === name);

Try / catch

null

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of siyuan-note/siyuan@afa823b6b4 (2026-08-18). Data as JSON: /api/errors/94d5f8aa7de0259c. Report an issue: GitHub.