siyuan-note/siyuan · error
path %v: value is %s
Error message
path %v: value is %s
What it means
Thrown by getJsContextValue when a path segment resolves to a JS undefined or null value (goja.IsUndefined || goja.IsNull). Unlike the nil case, the value exists in goja terms but is the JS absence value. The %s is cursor.String() ("undefined" or "null").
Source
Thrown at kernel/plugin/sandbox.go:298
if err := ObjectSetDataMethods(p, rt, obj, data); err != nil {
return nil, err
}
return obj, nil
}
// getJsContextValue safely retrieves a nested value from the plugin's JS context, returning nil if any step fails.
func getJsContextValue(rt *goja.Runtime, paths []any) (value goja.Value, err error) {
var cursor goja.Value = rt.GlobalObject()
var path string = "globalThis"
for _, key := range paths {
if cursor == nil {
err = fmt.Errorf("path %v: value is nil", key)
return
}
if goja.IsUndefined(cursor) || goja.IsNull(cursor) {
err = fmt.Errorf("path %v: value is %s", key, cursor.String())
return
}
obj := cursor.ToObject(rt)
if obj == nil {
err = fmt.Errorf("path %v: expected object, got %T", key, cursor)
return
}
switch k := key.(type) {
case string:
cursor = obj.Get(k)
path = fmt.Sprintf("%s.%s", path, k)
case int:
cursor = obj.Get(strconv.Itoa(k))
path = fmt.Sprintf("%s[%d]", path, k)
default:
err = fmt.Errorf("unsupported path type: %T", key)View on GitHub (pinned to 251596fc0d)
Solutions
- Confirm every scope/requestType the kernel will query is populated in globalThis.siyuan.server[scope][requestType] as a non-null object.
- Guard the plugin with typeof checks before assignment so partial registration cannot leave undefined holes.
- Match the plugin's registration to the kernel's AccessScope/RequestType string constants exactly (case-sensitive).
Example fix
// before
globalThis.siyuan.server = { network: { /* missing other scope */ } }
// after
const server = globalThis.siyuan.server || (globalThis.siyuan.server = {})
server.network = server.network || {}
server.network.beforeRequest = { handler: (req) => { ... } } Defensive patterns
Strategy: validation
Validate before calling
// Plugin side: ensure scope/requestType objects exist before assigning handler.
const server = (globalThis.siyuan.server ??= {})
const scope = (server['network'] ??= {})
if (scope['beforeRequest'] == null) scope['beforeRequest'] = {} Type guard
function scopeEntryExists(scope: string, rt: string): boolean {
const n = globalThis.siyuan?.server?.[scope]?.[rt]
return n !== undefined && n !== null
} Prevention
- Register every scope/requestType the kernel will query; do not leave holes.
- Use nullish-coalescing assignment to guarantee object creation at each level.
- Match scope/requestType strings to the kernel's AccessScope/RequestType constants (case-sensitive).
When it happens
Trigger: Traversing into globalThis.siyuan.server[scope][requestType] where an intermediate object exists but the requested property is undefined/null — e.g. siyuan.server is defined but the scope key was never populated, or event handler object is null.
Common situations: Plugin registers siyuan.server but forgets a specific AccessScope (e.g. registers "network" but kernel requests "filebox"); plugin sets a hook to null intentionally but kernel tries to read a sub-key; API contract mismatch between kernel and plugin.
Related errors
- path %v: value is nil
- path %v: expected object, got %T
- synchronous function returned a Promise
- expected promise object, got %T
- 'promise.then property is not a function
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/c3466ec13c093746.
Report an issue: GitHub.