siyuan-note/siyuan · error
globalThis.siyuan.server
Error message
globalThis.siyuan.server[%s][%s] is not an object
What it means
getRequestHandler walks the plugin's JS context globalThis.siyuan.server[scope][requestType] and converts the found value to a *goja.Object. If the property exists but ToObject yields nil (the value cannot be treated as an object), this error reports that the expected handler container is not an object.
Solutions
- Fix the plugin to register an object: siyuan.server[scope][requestType] = { handler: async (req) => ... }
- Verify with console.log(typeof globalThis.siyuan.server[scope][requestType]) that it is 'object' and not null
- Update the plugin to the version matching the current SiYuan plugin API
Example fix
// before
siyuan.server['plugin']['request'] = myHandler;
// after
siyuan.server['plugin']['request'] = { handler: myHandler }; Defensive patterns
Strategy: validation
Validate before calling
const node = globalThis.siyuan?.server?.[scope]?.[requestType]; if (typeof node !== 'object' || node === null) throw new Error(`siyuan.server[${scope}][${requestType}] must be an object`); Type guard
function isHandlerNode(v) { return typeof v === 'object' && v !== null && typeof v.handler === 'function'; } Try / catch
try { await request(scope, type); } catch (e) { if (String(e).includes('is not an object')) { reRegisterPluginHandlers(); } else { throw e; } } Prevention
- Register handlers as { handler: fn } objects, never bare values
- Log typeof at registration time to catch shape mistakes early
- Keep plugin registration code version-matched to the SiYuan plugin API
When it happens
Trigger: A plugin defines siyuan.server[scope][requestType] as a non-object value (string, number, function, boolean) instead of an object with a .handler member, when the sandbox resolves a registered request handler.
Common situations: Typos in plugin registration code (assigning the handler function directly instead of { handler: fn }), or a plugin version whose export shape changed.
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
- promise rejected
- 'promise.then property is not a function
- siyuan.server[ ][ ].handler is not set
- failed to serialize config.
- failed to serialize inputSchema
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/966e2083f869336a.
Report an issue: GitHub.
Appendix: 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 9f775e8a12)