siyuan-note/siyuan · error

third argument must be a handler function

Error message

third argument must be a handler function

What it means

Thrown by siyuan.agent.registerCapability when the third argument fails goja.AssertFunction — the handler is not callable. The handler is stored and later invoked by invokeAgentCapability when the AI model calls the tool, so it must be a JS function.

Source

Thrown at kernel/plugin/api_agent.go:131

						if effectsValue := configObj.Get("effects"); isJsValueNotNull(effectsValue) {
							if effects, err = jsCapabilityEffectsToGoEffects(rt, effectsValue); err != nil {
								return
							}
						}
						if actionEffectsValue := configObj.Get("actionEffects"); isJsValueNotNull(actionEffectsValue) {
							if actionEffects, err = jsCapabilityActionEffectsToGoEffects(rt, actionEffectsValue); err != nil {
								return
							}
						}
					}
				} else {
					err = fmt.Errorf("second argument must be a config object")
					return
				}
				if fn, ok := goja.AssertFunction(call.Argument(2)); ok {
					handler = fn
				} else {
					err = fmt.Errorf("third argument must be a handler function")
					return
				}
				return
			}
		}()

		runErr := p.worker.Run(func(rt *goja.Runtime) (result any, err error) {
			if argErr != nil {
				err = argErr
				return
			}

			fullToolName := pluginCapabilityModelName(p.Name, name)
			declaredActionEffects := make(map[string]tools.ToolEffects, len(actionEffects)+1)
			for action, actionEffect := range actionEffects {
				declaredActionEffects[action] = actionEffect
			}
			if effects != nil {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Pass an actual function reference as the third argument
  2. If using an arrow function or method, ensure you pass the function itself, not its name as a string

Example fix

// before
await siyuan.agent.registerCapability('myTool', config, 'handleTool');

// after
await siyuan.agent.registerCapability('myTool', config, (args) => {
  return { ok: true };
});
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof handler !== 'function') {
  throw new TypeError('Handler must be a function');
}
await siyuan.agent.registerCapability(name, config, handler);

Type guard

function isCallable(v) {
  return typeof v === 'function';
}

Prevention

When it happens

Trigger: Passing a non-function as the third argument: registerCapability('myTool', config, 'handler'), registerCapability('myTool', config, {}), or registerCapability('myTool', config, 42). Also fires if the argument is undefined.

Common situations: Plugin developer passes a string name of a function instead of the function reference; passes an object with methods instead of a bare function; the handler variable was destructured incorrectly.

Related errors


AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12). Data as JSON: /api/errors/6908244c3af3021b. Report an issue: GitHub.