siyuan-note/siyuan · error

globalThis.siyuan.server[%s][%s] is not an object

Error message

globalThis.siyuan.server[%s][%s] is not an object

What it means

Thrown by getRequestHandler after getJsContextValue successfully resolved globalThis.siyuan.server[scope][requestType] but the value's ToObject(rt) returned nil — the registered entry is not a JS object (likely a primitive). The handler registration is malformed at the scope/requestType level.

Source

Thrown at kernel/plugin/sandbox.go:504

		}
		return
	}
	err = fmt.Errorf("js value cannot be exported to a valid Go value")
	return
}

// 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
}

View on GitHub (pinned to 251596fc0d)

Solutions

  1. Wrap the handler in an object: globalThis.siyuan.server[scope][requestType] = { handler: fn }.
  2. Cross-check the plugin's registration against the kernel's getRequestHandler expectations (object with a `handler` callable).
  3. Ensure scope/requestType keys match the AccessScope/RequestType string constants exactly.

Example fix

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

Strategy: type-guard

Validate before calling

// Plugin side: ensure each scope/requestType entry is a wrapper object.
function registerHandler(scope: string, rt: string, fn: Function): void {
  const server = (globalThis.siyuan.server ??= {})
  const s = (server[scope] ??= {})
  s[rt] = { handler: fn }
}

Type guard

function isHandlerWrapper(v: unknown): v is { handler: Function } {
  return !!v && typeof v === 'object' && typeof (v as any).handler === 'function'
}

Prevention

When it happens

Trigger: A plugin sets globalThis.siyuan.server[scope][requestType] to a function, string, or other non-object instead of an object containing a `handler` property. getRequestHandler expects { handler: fn, ... }.

Common situations: Plugin author assigns the handler function directly (server.network.beforeRequest = fn) instead of wrapping it ({handler: fn}); tutorial/sample code used an outdated registration shape.

Related errors


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