siyuan-note/siyuan · error
'promise.then property is not a function
Error message
'promise.then property is not a function
What it means
invokeFunction confirmed the result is a Promise and ToObject succeeded, but obj.Get("then") is not callable via goja.AssertFunction — the object lacks a real .then method. This means a thenable-shaped object whose then is missing or not a function.
Source
Thrown at kernel/plugin/sandbox.go:394
callback(rt, &CallResult{Error: invokeErr})
return
}
result := resultJs.Export()
if isGoPromise(result) {
if !async {
panic(fmt.Errorf("synchronous function returned a Promise"))
}
resultObj := resultJs.ToObject(rt)
if resultObj == nil {
callback(rt, &CallResult{Error: fmt.Errorf("expected promise object, got %T", result)})
return
}
thenValue := resultObj.Get("then")
then, ok := goja.AssertFunction(thenValue)
if !ok {
callback(rt, &CallResult{Error: fmt.Errorf("'promise.then property is not a function")})
return
}
then(resultObj, rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) {
// ⚠️ call.Arguments always is an empty array.
promise, ok := result.(*goja.Promise)
if ok {
callback(rt, &CallResult{Value: promise.Result()})
} else {
callback(rt, &CallResult{Value: resultJs})
}
}), rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) {
callback(rt, &CallResult{Error: fmt.Errorf("promise rejected: %v", call.Argument(0).Export())})
}))
} else {
callback(rt, &CallResult{Value: resultJs})
}
}View on GitHub (pinned to 251596fc0d)
Solutions
- Return a genuine native Promise from the handler rather than a hand-built thenable.
- Verify Promise.prototype.then is intact in the sandbox (no monkey-patching that removes it).
- If returning a thenable, ensure it exposes a callable .then(onFulfilled, onRejected).
Example fix
// before
handler = () => ({ then: null })
// after
handler = () => new globalThis.Promise((resolve) => resolve(1)) Defensive patterns
Strategy: type-guard
Validate before calling
// Plugin side: verify .then is a function before returning a thenable.
function safeThenable(v: any): v is Promise<any> {
return v != null && typeof (v as any).then === 'function'
} Type guard
function hasCallableThen(v: unknown): v is { then: (...a: any[]) => any } {
return !!v && typeof (v as any)?.then === 'function'
} Prevention
- Return native Promises, not hand-built thenables.
- Do not delete or override Promise.prototype.then in the sandbox.
- If constructing a thenable, always expose a callable then(onFulfilled, onRejected).
When it happens
Trigger: A plugin returns an object that isGoPromise matched (e.g. has promise-like type) but whose .then was deleted, is undefined, or is a non-function property; a custom thenable with then: null; tampered Promise.prototype.then.
Common situations: Plugin overrides/deletes Promise.prototype.then; plugin returns a duck-typed thenable missing then; sandbox strips the then property for security.
Related errors
- expected promise object, got %T
- promise rejected: %v
- path %v: value is nil
- path %v: value is %s
- path %v: expected object, got %T
AI-assisted analysis of siyuan-note/siyuan@251596fc0d (2026-08-12).
Data as JSON: /api/errors/84f30f9707abab92.
Report an issue: GitHub.