siyuan-note/siyuan · error

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

Error message

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

What it means

getRequestHandler finds a non-null 'handler' property but goja.AssertFunction fails, meaning the property holds a value that is not callable (plain object, string, number, etc.). The sandbox requires handlers to be JS functions.

Solutions

  1. Ensure handler is a function: obj.handler = async (request) => ({...})
  2. Check typeof globalThis.siyuan.server[scope][requestType].handler === 'function' at registration time
  3. Look for other plugin code overwriting the same scope/requestType key

Example fix

// before
siyuan.server['plugin']['request'] = { handler: { method: 'POST' } };
// after
siyuan.server['plugin']['request'] = { handler: async (req) => ({ code: 0 }) };
Defensive patterns

Strategy: type-guard

Validate before calling

const h = globalThis.siyuan?.server?.[scope]?.[requestType]?.handler; if (typeof h !== 'function') throw new Error(`handler for ${scope}/${requestType} must be a function, got ${typeof h}`);

Type guard

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

Try / catch

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

Prevention

When it happens

Trigger: Assigning something non-callable to siyuan.server[scope][requestType].handler, e.g. { handler: { ... } }, { handler: 'string' }, or a value that got overwritten by other plugin code.

Common situations: Copy-paste mistakes in plugin registration, assigning a config object instead of the function, or a name collision where another plugin/property replaced the handler.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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

Appendix: 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 9f775e8a12)