siyuan-note/siyuan · error
path : expected object, got %T
Error message
path %v: expected object, got %T
What it means
While walking the property path, getJsContextValue converts the cursor to an object before reading the next key. If ToObject returns nil — the cursor is a value that cannot be represented as an object for property access — this error reports the key and the cursor's Go type. It flags an unexpected value shape at some point in the requested context path.
Solutions
- Inspect the %T in the message to identify what type occupies the path segment
- Restore the expected namespace to a proper object (re-run the relevant inject* function)
- Validate that each path segment is an object before resolving deeper
- Add a pre-check conversion (cursor.ToObject(rt) != nil) in custom dispatch code
Example fix
// before
getJsContextValue(rt, []any{"siyuan", "client"}) // error: expected object, got <nil>
// after
siyuan := rt.GlobalObject().Get("siyuan")
if siyuan == nil || siyuan.ToObject(rt) == nil {
return fmt.Errorf("siyuan namespace missing")
} Defensive patterns
Strategy: validation
Validate before calling
siyuan := rt.GlobalObject().Get("siyuan")
if siyuan == nil || siyuan.ToObject(rt) == nil { return errors.New("siyuan namespace is not an object") } Type guard
func isJSObject(rt *goja.Runtime, v goja.Value) bool {
return v != nil && !goja.IsUndefined(v) && !goja.IsNull(v) && v.ToObject(rt) != nil
} Try / catch
v, err := getJsContextValue(rt, paths)
if err != nil {
logging.LogWarnf("unexpected value in path: %v", err)
return
} Prevention
- Inspect the %T in the message to identify the offending value
- Re-run the relevant inject* function if a namespace was overwritten
- Validate each segment is an object before deeper traversal
When it happens
Trigger: Calling getJsContextValue where an intermediate value is of a Go/JS type whose ToObject yields nil — e.g. a primitive stub, an empty value produced by a failed earlier conversion, or a host object that refused conversion.
Common situations: A global namespace overwritten with a non-object primitive by plugin code; a host-injected value that failed conversion; path resolution hitting a getter that returned an exotic value.
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
- path : value is nil
- path : value is
- expected promise object, got %T
- failed to enable extend modules
- failed to inject global context
AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11).
Data as JSON: /api/errors/bb733b463bc94fd5.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/plugin/sandbox.go:304
// 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)
return
}
}
value = cursor
return
}View on GitHub (pinned to 8641553a1f)