{"record":{"id":"db4f2b27df69a683","repo":"siyuan-note/siyuan","slug":"path-v-value-is-nil","errorCode":null,"errorMessage":"path %v: value is nil","messagePattern":"path (.+?): value is nil","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/sandbox.go","lineNumber":293,"sourceCode":"}\n\n// NewDataObject creates a new JS object with text(), json(), buffer() and arrayBuffer() methods for the given data.\nfunc NewDataObject(p *KernelPlugin, rt *goja.Runtime, data []byte) (*goja.Object, error) {\n\tobj := rt.NewObject()\n\tif err := ObjectSetDataMethods(p, rt, obj, data); err != nil {\n\t\treturn nil, err\n\t}\n\treturn obj, nil\n}\n\n// getJsContextValue safely retrieves a nested value from the plugin's JS context, returning nil if any step fails.\nfunc getJsContextValue(rt *goja.Runtime, paths []any) (value goja.Value, err error) {\n\tvar cursor goja.Value = rt.GlobalObject()\n\tvar path string = \"globalThis\"\n\n\tfor _, key := range paths {\n\t\tif cursor == nil {\n\t\t\terr = fmt.Errorf(\"path %v: value is nil\", key)\n\t\t\treturn\n\t\t}\n\n\t\tif goja.IsUndefined(cursor) || goja.IsNull(cursor) {\n\t\t\terr = fmt.Errorf(\"path %v: value is %s\", key, cursor.String())\n\t\t\treturn\n\t\t}\n\n\t\tobj := cursor.ToObject(rt)\n\t\tif obj == nil {\n\t\t\terr = fmt.Errorf(\"path %v: expected object, got %T\", key, cursor)\n\t\t\treturn\n\t\t}\n\n\t\tswitch k := key.(type) {\n\t\tcase string:\n\t\t\tcursor = obj.Get(k)\n\t\t\tpath = fmt.Sprintf(\"%s.%s\", path, k)","sourceCodeStart":275,"sourceCodeEnd":311,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/sandbox.go#L275-L311","documentation":"Thrown by getJsContextValue when traversing the plugin's JS context (globalThis.siyuan.*) and the cursor along the path is a Go nil goja.Value before reaching the requested key. This indicates a parent segment in the lookup chain was never assigned (obj.Get returned nil) rather than being JS undefined/null. The %v is the path key being looked up when nil was encountered.","triggerScenarios":"Calling getJsContextValue(rt, []any{\"siyuan\", \"server\", scope, requestType}) or getJsContextValue(rt, []any{\"siyuan\", \"event\"}) when a previous obj.Get returned nil — e.g. globalThis.siyuan itself is absent, or siyuan.server is nil. Happens during dispatchEvent or getRequestHandler when the plugin did not register the expected global.","commonSituations":"A plugin's main JS fails to set window/globalThis.siyuan = {...}; a plugin targets an older API shape; a sandboxed plugin whose top-level code threw before assignment; runtime reload after partial init.","solutions":["Ensure the plugin JS assigns globalThis.siyuan with the documented structure (event.on, server[scope][requestType].handler) before any request fires.","Inspect the plugin's init/entrypoint for early throws that abort before globalThis.siyuan is assigned.","Reproduce by logging rt.GlobalObject().Get(\"siyuan\") right before the failing call to confirm the nil segment.","Validate the plugin manifest/API version matches the kernel's expected global shape."],"exampleFix":"// before (plugin entry, assignment aborted by earlier throw)\nglobalThis.siyuan = {}\n// after\nif (typeof globalThis.siyuan !== 'object' || globalThis.siyuan === null) globalThis.siyuan = {}\nglobalThis.siyuan.event = { on(e) { ... } }","handlingStrategy":"validation","validationCode":"// Before invoking a kernel hook path, confirm the global exists and is object.\n// (Plugin side, at registration time)\nif (typeof globalThis.siyuan !== 'object' || globalThis.siyuan === null) {\n  globalThis.siyuan = {}\n}","typeGuard":"function isSiyuanGlobalObject(): boolean {\n  return typeof globalThis.siyuan === 'object' && globalThis.siyuan !== null\n}","tryCatchPattern":null,"preventionTips":["Always initialize globalThis.siyuan = {} at the very top of plugin entry, before any code that can throw.","Treat every kernel-traversed path segment as required: siyuan, siyuan.server, siyuan.event.","Log a warning if a required segment is missing at load time rather than letting the kernel fail later."],"tags":["plugin","goja","javascript-runtime","sandbox"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}