siyuan-note/siyuan · error

siyuan.server[ ][ ].handler is not set

Error message

siyuan.server[%s][%s].handler is not set

What it means

After locating the handler container siyuan.server[scope][requestType], getRequestHandler reads its 'handler' property; if it is null, undefined, or missing (isJsValueNotNull fails), the plugin has not provided a handler and this error is returned.

Solutions

  1. Implement and assign the handler: obj.handler = async (request) => response
  2. Register only the request types the plugin actually handles; check the requested scope/requestType spelling
  3. Ensure the plugin's registration code runs before the first incoming request (e.g. during plugin load/open)

Example fix

// before
siyuan.server['plugin']['fetch'] = {};
// after
siyuan.server['plugin']['fetch'] = { handler: async (req) => ({ code: 0, data: null }) };
Defensive patterns

Strategy: validation

Validate before calling

const node = globalThis.siyuan?.server?.[scope]?.[requestType]; if (!node || node.handler == null) throw new Error(`handler for ${scope}/${requestType} is not registered`);

Type guard

function hasHandler(v) { return v != null && v.handler != null; }

Try / catch

try { await request(scope, type); } catch (e) { if (String(e).includes('.handler is not set')) { showPluginNotReadyMessage(); } else { throw e; } }

Prevention

When it happens

Trigger: A plugin registers siyuan.server[scope][requestType] as an object but leaves 'handler' unset, or the incoming request's scope/requestType combination has no handler registered while the container object exists (possibly empty).

Common situations: Incomplete plugin implementation, a request type the plugin intended to support but never registered, or the container created by scaffolding with a TODO placeholder.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19). Data as JSON: /api/errors/ad1826c7c9be1b81. Report an issue: GitHub.

Appendix: source

Thrown at kernel/plugin/sandbox.go:511

// getRequestHandler retrieves the handler function and its containing object for a given scope and request type from the plugin's JS context.
func getRequestHandler(rt *goja.Runtime, scope AccessScope, requestType RequestType) (handler goja.Callable, handlerObj *goja.Object, err error) {
	// Get handler object: siyuan.server[scope][requestType]
	handlerObjValue, getObjErr := getJsContextValue(rt, []any{"siyuan", "server", string(scope), string(requestType)})
	if getObjErr != nil {
		err = getObjErr
		return
	}

	handlerObj = handlerObjValue.ToObject(rt)
	if handlerObj == nil {
		err = fmt.Errorf("globalThis.siyuan.server[%s][%s] is not an object", scope, requestType)
		return
	}

	// Get handler: siyuan.server[scope][requestType].handler
	handlerValue := handlerObj.Get("handler")
	if !isJsValueNotNull(handlerValue) {
		err = fmt.Errorf("siyuan.server[%s][%s].handler is not set", scope, requestType)
		return
	}

	handler, ok := goja.AssertFunction(handlerValue)
	if !ok {
		err = fmt.Errorf("siyuan.server[%s][%s].handler is not a function", scope, requestType)
		return
	}

	return
}

// requestGoToJs converts a Go Request to a JavaScript value.
func requestGoToJs(p *KernelPlugin, rt *goja.Runtime, request *Request) (jsRequest goja.Value, err error) {
	// convert body raw data to js object
	if data, ok := request.Request.Body.Data.(*[]byte); ok && data != nil {
		request.Request.Body.Data, err = NewDataObject(p, rt, *data)
		if err != nil {

View on GitHub (pinned to 9f775e8a12)