{"record":{"id":"ad1826c7c9be1b81","repo":"siyuan-note/siyuan","slug":"siyuan-server-s-s-handler-is-not-set","errorCode":null,"errorMessage":"siyuan.server[%s][%s].handler is not set","messagePattern":"siyuan\\.server\\[(.+?)\\]\\[(.+?)\\]\\.handler is not set","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/sandbox.go","lineNumber":511,"sourceCode":"// 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}\n\n// requestGoToJs converts a Go Request to a JavaScript value.\nfunc requestGoToJs(p *KernelPlugin, rt *goja.Runtime, request *Request) (jsRequest goja.Value, err error) {\n\t// convert body raw data to js object\n\tif data, ok := request.Request.Body.Data.(*[]byte); ok && data != nil {\n\t\trequest.Request.Body.Data, err = NewDataObject(p, rt, *data)\n\t\tif err != nil {","sourceCodeStart":493,"sourceCodeEnd":529,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/plugin/sandbox.go#L493-L529","documentation":"After locating the handler container siyuan.server[scope][requestType], getRequestHandler reads its 'handler' property; if it is null, undefined, or missing (isJsValueNotNull fails), the plugin has not provided a handler and this error is returned.","triggerScenarios":"A plugin registers siyuan.server[scope][requestType] as an object but leaves 'handler' unset, or the incoming request's scope/requestType combination has no handler registered while the container object exists (possibly empty).","commonSituations":"Incomplete plugin implementation, a request type the plugin intended to support but never registered, or the container created by scaffolding with a TODO placeholder.","solutions":["Implement and assign the handler: obj.handler = async (request) => response","Register only the request types the plugin actually handles; check the requested scope/requestType spelling","Ensure the plugin's registration code runs before the first incoming request (e.g. during plugin load/open)"],"exampleFix":"// before\nsiyuan.server['plugin']['fetch'] = {};\n// after\nsiyuan.server['plugin']['fetch'] = { handler: async (req) => ({ code: 0, data: null }) };","handlingStrategy":"validation","validationCode":"const node = globalThis.siyuan?.server?.[scope]?.[requestType]; if (!node || node.handler == null) throw new Error(`handler for ${scope}/${requestType} is not registered`);","typeGuard":"function hasHandler(v) { return v != null && v.handler != null; }","tryCatchPattern":"try { await request(scope, type); } catch (e) { if (String(e).includes('.handler is not set')) { showPluginNotReadyMessage(); } else { throw e; } }","preventionTips":["Assign handler during plugin load before serving requests","Register only request types the plugin implements; verify spellings","Add a self-check after registration that every intended handler exists"],"tags":["goja","plugin","registration","missing-handler"],"backgroundTag":"missing-required-argument","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}