{"record":{"id":"29a1d97cd6800623","repo":"GopeedLab/gopeed","slug":"promise-then-is-not-callable-29a1d9","errorCode":null,"errorMessage":"promise.then is not callable","messagePattern":"promise\\.then is not callable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/download/engine/inject/stream/module.go","lineNumber":511,"sourceCode":"\t\t}()\n\t\tvalue, err := fn(runtime)\n\t\tif err != nil {\n\t\t\tsend(nil, err)\n\t\t\treturn\n\t\t}\n\t\tif promise, ok := value.Export().(*goja.Promise); ok {\n\t\t\tswitch promise.State() {\n\t\t\tcase goja.PromiseStateFulfilled:\n\t\t\t\tsend(promise.Result(), nil)\n\t\t\t\treturn\n\t\t\tcase goja.PromiseStateRejected:\n\t\t\t\tsend(nil, exportJSError(promise.Result()))\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t\tthenVal := value.ToObject(runtime).Get(\"then\")\n\t\t\t\tthenFn, ok := goja.AssertFunction(thenVal)\n\t\t\t\tif !ok {\n\t\t\t\t\tsend(nil, errors.New(\"promise.then is not callable\"))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tonFulfilled := runtime.ToValue(func(call goja.FunctionCall) goja.Value {\n\t\t\t\t\tsend(call.Argument(0), nil)\n\t\t\t\t\treturn goja.Undefined()\n\t\t\t\t})\n\t\t\t\tonRejected := runtime.ToValue(func(call goja.FunctionCall) goja.Value {\n\t\t\t\t\tsend(nil, exportJSError(call.Argument(0)))\n\t\t\t\t\treturn goja.Undefined()\n\t\t\t\t})\n\t\t\t\tif _, err := thenFn(value, onFulfilled, onRejected); err != nil {\n\t\t\t\t\tsend(nil, err)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tsend(value, nil)\n\t})","sourceCodeStart":493,"sourceCodeEnd":529,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/pkg/download/engine/inject/stream/module.go#L493-L529","documentation":"Same broken-thenable check as the main engine, but inside the stream inject module's loop helper (pkg/download/engine/inject/stream/module.go:499-526): a value returned by stream-related script code exports as *goja.Promise, is still pending, and value.ToObject(runtime).Get(\"then\") is not callable (goja.AssertFunction fails). The stream module then aborts the operation with 'promise.then is not callable'.","triggerScenarios":"Stream/fetch polyfill code in a resolver script returns a fake thenable ({then: 'not-a-fn'}) or an object masquerading as a promise; the script overrides Promise with a non-standard implementation whose instances lack callable then; a Go-provided stream object is returned raw where a promise was expected, and its 'then' JSON field is not a function.","commonSituations":"Resolver scripts replacing or monkey-patching globalThis.Promise; returning the stream object from an async wrapper incorrectly; polyfills copied from Node/browsers relying on thenables goja cannot drive; field-name mismatches under the json tag mapper so 'then' resolves to undefined.","solutions":["Return genuine promises from stream script code — async functions or the engine's own fetch/stream APIs","Do not override Promise in scripts run by this engine","If a custom thenable is required, give it a real function then(onFulfilled, onRejected)","Log the offending value (typeof value, Object.keys(value)) from a debug copy of the script to identify what lacks then"],"exampleFix":"// before (script)\nconst res = { then: null, body: stream }\nreturn res\n\n// after (script)\nreturn Promise.resolve(stream)","handlingStrategy":"try-catch","validationCode":"// Script-side: return genuine promises from stream code\n// return Promise.resolve(streamObj) — never {then: <non-function>}","typeGuard":"// Script-side:\nif (v != null && typeof v === 'object' && typeof v.then !== 'function') {\n    v = Promise.resolve(v)\n}","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"promise.then is not callable\") {\n    // stream script returned a broken thenable: fix the script's return value\n}","preventionTips":["Use the engine's own fetch/stream APIs instead of reimplementing thenables","Never monkey-patch Promise inside injected scripts","Log returned values (typeof/keys) when developing resolver scripts"],"tags":["javascript","goja","promise","stream","engine"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}