{"record":{"id":"98b6783ae7b4f15f","repo":"siyuan-note/siyuan","slug":"promise-rejected-v","errorCode":null,"errorMessage":"promise rejected: %v","messagePattern":"promise rejected: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/plugin/sandbox.go","lineNumber":407,"sourceCode":"\t\t}\n\n\t\tthenValue := resultObj.Get(\"then\")\n\t\tthen, ok := goja.AssertFunction(thenValue)\n\t\tif !ok {\n\t\t\tcallback(rt, &CallResult{Error: fmt.Errorf(\"'promise.then property is not a function\")})\n\t\t\treturn\n\t\t}\n\n\t\tthen(resultObj, rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) {\n\t\t\t// ⚠️ call.Arguments always is an empty array.\n\t\t\tpromise, ok := result.(*goja.Promise)\n\t\t\tif ok {\n\t\t\t\tcallback(rt, &CallResult{Value: promise.Result()})\n\t\t\t} else {\n\t\t\t\tcallback(rt, &CallResult{Value: resultJs})\n\t\t\t}\n\t\t}), rt.ToValue(func(call goja.FunctionCall, rt *goja.Runtime) {\n\t\t\tcallback(rt, &CallResult{Error: fmt.Errorf(\"promise rejected: %v\", call.Argument(0).Export())})\n\t\t}))\n\t} else {\n\t\tcallback(rt, &CallResult{Value: resultJs})\n\t}\n}\n\n// isJsPromise checks if a goja.Value is a JavaScript Promise.\nfunc isJsPromise(jsValue goja.Value) bool {\n\tif jsValue == nil {\n\t\treturn false\n\t}\n\n\tgoValue := jsValue.Export()\n\treturn isGoPromise(goValue)\n}\n\n// isGoPromise checks if a Go value is a *goja.Promise.\nfunc isGoPromise(goValue any) bool {","sourceCodeStart":389,"sourceCodeEnd":425,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/plugin/sandbox.go#L389-L425","documentation":"The handler returned a Promise and invokeFunction attached onFulfilled/onRejected callbacks via .then; the rejection callback fired, so the plugin's async operation rejected. %v is call.Argument(0).Export() — the rejection reason (an Error, string, or value).","triggerScenarios":"An async plugin handler calls reject, throws inside an async function, or awaits a rejected Promise. Surfaced through the registered server/event async hook during request processing.","commonSituations":"Plugin's fetch to an external service fails (network/404); plugin throws inside async handler due to bad input; uncaught rejection in a chained .then inside the handler.","solutions":["Inspect the rejection reason in the error message (the %v) to find the root cause.","Add try/catch inside the async handler and return a structured error or a safe default instead of rejecting.","Validate inputs before performing the async operation that rejects."],"exampleFix":"// before\nhandler = async (req) => { return await fetch(req.url) } // rejects on network error\n// after\nhandler = async (req) => {\n  try { return await fetch(req.url) }\n  catch (e) { return { error: e.message } } // resolves instead of rejecting\n}","handlingStrategy":"try-catch","validationCode":"// Validate inputs that commonly cause rejection before the async op.\nfunction validateRequest(req: { url?: string }): void {\n  if (!req.url) throw new Error('missing url')\n  try { new URL(req.url) } catch { throw new Error('invalid url') }\n}","typeGuard":null,"tryCatchPattern":"// Resolve-with-structured-error instead of letting the Promise reject.\nhandler = async (req) => {\n  try { return await doAsync(req) }\n  catch (e) { return { ok: false, error: String(e) } }\n}","preventionTips":["Wrap async handler bodies in try/catch and resolve with a structured result.","Validate inputs (URLs, IDs) before the async call that can reject.","Log the rejection reason locally so root cause is visible without kernel logs."],"tags":["plugin","goja","javascript-runtime","promise","async","rejection"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}