{"record":{"id":"966e2083f869336a","repo":"siyuan-note/siyuan","slug":"globalthis-siyuan-server-s-s-is-not-an-object","errorCode":null,"errorMessage":"globalThis.siyuan.server[%s][%s] is not an object","messagePattern":"globalThis\\.siyuan\\.server\\[(.+?)\\]\\[(.+?)\\] is not an object","errorType":"exception","errorClass":null,"httpStatus":500,"severity":"error","filePath":"kernel/plugin/sandbox.go","lineNumber":504,"sourceCode":"\t\t}\n\t\treturn\n\t}\n\terr = fmt.Errorf(\"js value cannot be exported to a valid Go value\")\n\treturn\n}\n\n// getRequestHandler retrieves the handler function and its containing object for a given scope and request type from the plugin's JS context.\nfunc getRequestHandler(rt *goja.Runtime, scope AccessScope, requestType RequestType) (handler goja.Callable, handlerObj *goja.Object, err error) {\n\t// Get handler object: siyuan.server[scope][requestType]\n\thandlerObjValue, getObjErr := getJsContextValue(rt, []any{\"siyuan\", \"server\", string(scope), string(requestType)})\n\tif getObjErr != nil {\n\t\terr = getObjErr\n\t\treturn\n\t}\n\n\thandlerObj = handlerObjValue.ToObject(rt)\n\tif handlerObj == nil {\n\t\terr = fmt.Errorf(\"globalThis.siyuan.server[%s][%s] is not an object\", scope, requestType)\n\t\treturn\n\t}\n\n\t// Get handler: siyuan.server[scope][requestType].handler\n\thandlerValue := handlerObj.Get(\"handler\")\n\tif !isJsValueNotNull(handlerValue) {\n\t\terr = fmt.Errorf(\"siyuan.server[%s][%s].handler is not set\", scope, requestType)\n\t\treturn\n\t}\n\n\thandler, ok := goja.AssertFunction(handlerValue)\n\tif !ok {\n\t\terr = fmt.Errorf(\"siyuan.server[%s][%s].handler is not a function\", scope, requestType)\n\t\treturn\n\t}\n\n\treturn\n}","sourceCodeStart":486,"sourceCodeEnd":522,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/sandbox.go#L486-L522","documentation":"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.","triggerScenarios":"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, ... }.","commonSituations":"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.","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."],"exampleFix":"// before\nglobalThis.siyuan.server.network.beforeRequest = (req) => { /*...*/ }\n// after\nglobalThis.siyuan.server.network.beforeRequest = { handler: (req) => { /*...*/ } }","handlingStrategy":"type-guard","validationCode":"// Plugin side: ensure each scope/requestType entry is a wrapper object.\nfunction registerHandler(scope: string, rt: string, fn: Function): void {\n  const server = (globalThis.siyuan.server ??= {})\n  const s = (server[scope] ??= {})\n  s[rt] = { handler: fn }\n}","typeGuard":"function isHandlerWrapper(v: unknown): v is { handler: Function } {\n  return !!v && typeof v === 'object' && typeof (v as any).handler === 'function'\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["plugin","goja","javascript-runtime","server-hooks","type-mismatch"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}