GopeedLab/gopeed · error
promise rejected
Error message
promise rejected
What it means
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.
Source
Thrown at pkg/download/engine/engine.go:210
<-done
return engine
}
func Run(script string) (value any, err error) {
engine := NewEngine(nil)
return engine.RunString(script)
}
func exportJSValue(value goja.Value) any {
if value == nil {
return nil
}
return value.Export()
}
func exportJSError(value goja.Value) error {
if value == nil || goja.IsUndefined(value) || goja.IsNull(value) {
return errors.New("promise rejected")
}
if err, ok := value.Export().(error); ok {
return err
}
stack := value.String()
if ro, ok := value.(*goja.Object); ok {
stackVal := ro.Get("stack")
if stackVal != nil && stackVal.String() != "" {
stack = stackVal.String()
}
}
return errors.New(stack)
}
func callExportedFunction(fn func(goja.FunctionCall) goja.Value, args ...goja.Value) (ret goja.Value, err error) {
defer func() {
if r := recover(); r != nil {
switch v := r.(type) {View on GitHub (pinned to 7b7327ffb3)
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
Example fix
// before (script)
async function resolve() { throw undefined }
// after (script)
async function resolve() { throw new Error('resolve: no stream url found') } Defensive patterns
Strategy: try-catch
Validate before calling
// Script-side: reject with a reason so the Go side gets a real message
// Promise.reject(new Error('why')) instead of Promise.reject() Type guard
// Script-side guard:
function fail(msg) { throw msg == null ? new Error('failure (no reason given)') : msg } Try / catch
_, err := engine.RunString(script)
if err != nil && err.Error() == "promise rejected" {
// rejection carried undefined/null: instrument the script's .catch to surface the cause
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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'.
Related errors
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/2a95b0701c2237e1.
Report an issue: GitHub.