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
- Wrap the handler in an object: globalThis.siyuan.server[scope][requestType] = { handler: fn }.
- Cross-check the plugin's registration against the kernel's getRequestHandler expectations (object with a `handler` callable).
- 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
- Always wrap handler in { handler: fn }, never assign the function directly.
- Use a single registerHelper function in the plugin to enforce the wrapper shape.
- Cross-check registration shape against getRequestHandler's expectations.
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
- siyuan.server[%s][%s].handler is not a function
- path %v: expected object, got %T
- expected promise object, got %T
- siyuan.server[%s][%s].handler is not set
- path %v: value is nil
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/966e2083f869336a.
Report an issue: GitHub.