siyuan-note/siyuan · error

siyuan.server[%s][%s].handler is not a function

Error message

siyuan.server[%s][%s].handler is not a function

What it means

Thrown by getRequestHandler when globalThis.siyuan.server[scope][requestType].handler exists and is non-null but goja.AssertFunction fails — the handler is present but not a JS function (e.g. a string, object, or number). The handler callable contract is violated.

Source

Thrown at kernel/plugin/sandbox.go:517

		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 {
			return
		}
	}

	// convert body form files data to js object
	if request.Request.Body.Form != nil {

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Assign an actual function to the handler property.
  2. After refactor, confirm the referenced symbol is still a function and not shadowed by a string/config.
  3. Add a typeof === 'function' guard in the plugin before registration.

Example fix

// before
globalThis.siyuan.server.network.beforeRequest = { handler: 'onBeforeRequest' }
// after
globalThis.siyuan.server.network.beforeRequest = { handler: (req) => { /*...*/ } }
Defensive patterns

Strategy: type-guard

Validate before calling

// Plugin side: ensure handler is a function before assigning.
function setHandler(scope: string, rt: string, fn: unknown): void {
  if (typeof fn !== 'function') throw new Error('handler must be a function')
  globalThis.siyuan.server[scope][rt] = { handler: fn }
}

Type guard

function isCallable(v: unknown): v is (...a: any[]) => any {
  return typeof v === 'function'
}

Prevention

When it happens

Trigger: Plugin assigns handler to a non-function value: server.network.beforeRequest = { handler: 'doStuff' } or { handler: 42 } or { handler: {} }.

Common situations: Plugin author stored a config/label in handler by mistake; refactor renamed a function and left a stale non-function reference; serialization stored a string identifier instead of the function.

Related errors


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