siyuan-note/siyuan · error
promise rejected
Error message
promise rejected: %v
What it means
invokeFunction awaits the plugin handler's Promise by attaching a rejection callback via then(); when the Promise rejects, the rejection reason (exported to Go) is wrapped in this error and delivered through the CallResult. It is the sandbox's way of surfacing a failed async handler to the kernel caller.
Solutions
- Read the %v portion to see the original rejection reason thrown by the plugin
- Fix the underlying exception in the plugin's handler code (often a null/undefined access or a failed network call)
- Add try/catch inside the plugin handler to convert expected failures into normal error responses
Example fix
// before - plugin handler
async function handler(req) { return await riskyFetch(req); }
// after
async function handler(req) {
try { return await riskyFetch(req); }
catch (e) { return { code: -1, msg: String(e) }; }
} Defensive patterns
Strategy: try-catch
Validate before calling
null
Type guard
null
Try / catch
try { const r = await callPluginHandler(); } catch (e) { if (String(e).startsWith('promise rejected:')) { const reason = String(e).slice('promise rejected:'.length); log(reason); } else { throw e; } } Prevention
- Wrap async handler bodies in try/catch and return structured error responses
- Log rejection reasons inside the plugin before they propagate
- Test handlers with failing inputs to confirm graceful rejection
When it happens
Trigger: An async plugin handler (siyuan.server[scope][requestType].handler) throws inside its async body or its returned Promise rejects with any JS value (Error, string, object) - the exported rejection value appears in the %v placeholder.
Common situations: Plugin code throwing inside async request handlers (e.g. failed fetch in a server-scope handler, unhandled null dereference in an awaited call, network failures from within the plugin).
Related errors
- globalThis.siyuan.server
- 'promise.then property is not a function
- failed to serialize config.
- failed to serialize inputSchema
- globalThis.siyuan.plugin.lifecycle is not an object
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/98b6783ae7b4f15f.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/plugin/sandbox.go:407
}
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})
}
}
// isJsPromise checks if a goja.Value is a JavaScript Promise.
func isJsPromise(jsValue goja.Value) bool {
if jsValue == nil {
return false
}
goValue := jsValue.Export()
return isGoPromise(goValue)
}
// isGoPromise checks if a Go value is a *goja.Promise.
func isGoPromise(goValue any) bool {View on GitHub (pinned to 9f775e8a12)