{"record":{"id":"2a95b0701c2237e1","repo":"GopeedLab/gopeed","slug":"promise-rejected","errorCode":null,"errorMessage":"promise rejected","messagePattern":"promise rejected","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/download/engine/engine.go","lineNumber":210,"sourceCode":"\t<-done\n\treturn engine\n}\n\nfunc Run(script string) (value any, err error) {\n\tengine := NewEngine(nil)\n\treturn engine.RunString(script)\n}\n\nfunc exportJSValue(value goja.Value) any {\n\tif value == nil {\n\t\treturn nil\n\t}\n\treturn value.Export()\n}\n\nfunc exportJSError(value goja.Value) error {\n\tif value == nil || goja.IsUndefined(value) || goja.IsNull(value) {\n\t\treturn errors.New(\"promise rejected\")\n\t}\n\tif err, ok := value.Export().(error); ok {\n\t\treturn err\n\t}\n\tstack := value.String()\n\tif ro, ok := value.(*goja.Object); ok {\n\t\tstackVal := ro.Get(\"stack\")\n\t\tif stackVal != nil && stackVal.String() != \"\" {\n\t\t\tstack = stackVal.String()\n\t\t}\n\t}\n\treturn errors.New(stack)\n}\n\nfunc callExportedFunction(fn func(goja.FunctionCall) goja.Value, args ...goja.Value) (ret goja.Value, err error) {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tswitch v := r.(type) {","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/pkg/download/engine/engine.go#L192-L228","documentation":"exportJSError (pkg/download/engine/engine.go:203-218) converts a JS rejection value into a Go error. When the promise rejects with undefined or null (or the Go side passes a nil goja.Value), there is no message or stack to extract, so it falls back to the generic 'promise rejected'. Any richer information (Error object with .stack, a string, an error export) would have been preserved.","triggerScenarios":"Script calls Promise.reject() with no argument; 'throw undefined' / 'throw null' inside an async function or promise chain; reject(void 0) in minified/bundled code; calling a rejection callback with no args.","commonSituations":"Bundled/minified resolver scripts that throw bare undefined; scripts copied from environments where throw; undefined is idiomatic; debugging blind because the real cause is lost — the generic message means 'the script failed but gave no reason'.","solutions":["Re-run the failing script with logging or a try/catch wrapper that stringifies the rejection: p.catch(e => console.log(String(e), e && e.stack))","Change script code to always reject/throw with an Error: throw new Error('reason') or Promise.reject(new Error('reason')) so exportJSError can carry the message/stack","Temporarily wrap the script: Promise.resolve(scriptResult).catch(e => { throw e === undefined ? new Error('rejected with undefined') : e })","Once the real message appears, fix the underlying script bug"],"exampleFix":"// before (script)\nasync function resolve() { throw undefined }\n\n// after (script)\nasync function resolve() { throw new Error('resolve: no stream url found') }","handlingStrategy":"try-catch","validationCode":"// Script-side: reject with a reason so the Go side gets a real message\n// Promise.reject(new Error('why')) instead of Promise.reject()","typeGuard":"// Script-side guard:\nfunction fail(msg) { throw msg == null ? new Error('failure (no reason given)') : msg }","tryCatchPattern":"_, err := engine.RunString(script)\nif err != nil && err.Error() == \"promise rejected\" {\n    // rejection carried undefined/null: instrument the script's .catch to surface the cause\n}","preventionTips":["Always reject/throw Error objects with messages in scripts","Wrap third-party scripts so rejections are normalized to Errors","When you see the bare 'promise rejected', assume a script bug and add .catch logging"],"tags":["javascript","promise","diagnostics","engine"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}