{"record":{"id":"b58f165a4d277103","repo":"GopeedLab/gopeed","slug":"blob-runtime-is-not-available","errorCode":null,"errorMessage":"blob runtime is not available","messagePattern":"blob runtime is not available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/download/extension_runtime_webview.go","lineNumber":53,"sourceCode":"\t\t}\n\t\tif gopeed.Runtime.WebView != nil {\n\t\t\tif err := runtimeObject.Set(\"webview\", newJSWebViewRuntime(vm, gopeed.Runtime.WebView)); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\tif err := gopeedObject.Set(\"runtime\", runtimeObject); err != nil {\n\t\treturn err\n\t}\n\treturn vm.Set(\"gopeed\", gopeedObject)\n}\n\nfunc newJSBlobRuntime(vm *goja.Runtime) *goja.Object {\n\tobj := vm.NewObject()\n\t_ = obj.Set(\"createObjectURL\", func(call goja.FunctionCall) goja.Value {\n\t\tfn, ok := goja.AssertFunction(vm.Get(\"__gopeed_blob_create_object_url\"))\n\t\tif !ok {\n\t\t\tpanic(vm.ToValue(fmt.Errorf(\"blob runtime is not available\")))\n\t\t}\n\t\tvalue, err := fn(goja.Undefined(), call.Argument(0), call.Argument(1))\n\t\tif err != nil {\n\t\t\tpanic(vm.ToValue(err))\n\t\t}\n\t\treturn value\n\t})\n\t_ = obj.Set(\"revokeObjectURL\", func(call goja.FunctionCall) goja.Value {\n\t\tfn, ok := goja.AssertFunction(vm.Get(\"__gopeed_blob_revoke_object_url\"))\n\t\tif !ok {\n\t\t\tpanic(vm.ToValue(fmt.Errorf(\"blob runtime is not available\")))\n\t\t}\n\t\tif _, err := fn(goja.Undefined(), call.Argument(0)); err != nil {\n\t\t\tpanic(vm.ToValue(err))\n\t\t}\n\t\treturn goja.Undefined()\n\t})\n\treturn obj","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/GopeedLab/gopeed/blob/7b7327ffb30816273a74b142cccc0bc10c5a4c67/pkg/download/extension_runtime_webview.go#L35-L71","documentation":"Thrown when extension JavaScript calls gopeed.runtime.blob.createObjectURL() but the engine VM does not have a callable global __gopeed_blob_create_object_url. That global is installed by the stream polyfill embedded at pkg/download/engine/inject/stream/stream.js:863 and enabled via stream.Enable during engine construction (pkg/download/engine/engine.go:187). The blob bridge itself is exposed whenever gopeed.Runtime is non-nil (extension_runtime_webview.go:32-35), so any VM where the bridge exists but the stream module was never injected (or failed silently) panics with this message on first use.","triggerScenarios":"Calling gopeed.runtime.blob.createObjectURL(blob, opts) in an extension whose goja runtime lacks the stream module: a bare goja.Runtime passed to injectGopeed by an embedder, an engine whose stream.Enable returned an error (the error is swallowed at engine.go:187-189), extension code that overwrote or deleted globalThis.__gopeed_blob_create_object_url with a non-function, or a host built from a fork/older revision whose stream.js predates the blob globals.","commonSituations":"Embedding the gopeed extension engine into another app without going through Downloader-side engine construction; upgrading only part of a fork so the JS polyfill and Go bridge drift out of sync; extensions that enumerate and clear globalThis properties for sandboxing and accidentally wipe __gopeed_* internals.","solutions":["Run extensions only inside engines created by newExtensionEngine/NewEngine, which always call stream.Enable before extension code executes","Upgrade the host gopeed build so stream.js defines __gopeed_blob_create_object_url (present at stream.js:863)","Feature-detect before calling: skip the blob code path when typeof globalThis.__gopeed_blob_create_object_url !== 'function'","Audit extension code for assignments/deletes of __gopeed_* globals and remove them"],"exampleFix":"// before\nconst url = gopeed.runtime.blob.createObjectURL(blob);\n\n// after\nif (typeof globalThis.__gopeed_blob_create_object_url !== \"function\") {\n  throw new Error(\"blob runtime is unavailable on this host build\");\n}\nconst url = gopeed.runtime.blob.createObjectURL(blob);","handlingStrategy":"type-guard","validationCode":"if (typeof globalThis.__gopeed_blob_create_object_url !== \"function\") {\n  gopeed.logger.warn(\"blob runtime unavailable; skipping object URL creation\");\n  return;\n}","typeGuard":"function blobRuntimeAvailable() {\n  return typeof globalThis.__gopeed_blob_create_object_url === \"function\";\n}","tryCatchPattern":"try {\n  const url = gopeed.runtime.blob.createObjectURL(blob);\n} catch (e) {\n  if (String(e.message ?? e).includes(\"blob runtime is not available\")) {\n    gopeed.logger.warn(\"host lacks blob runtime\");\n  } else {\n    throw e;\n  }\n}","preventionTips":["Run extensions only on engines built by the downloader so the stream polyfill is always injected","Never assign to or delete __gopeed_* globals in extension code","Feature-detect blob support once at extension startup and cache the result","Keep host and extension API versions in sync when running a fork"],"tags":["extension","blob","javascript","goja","runtime"],"backgroundTag":null,"analyzedSha":"7b7327ffb30816273a74b142cccc0bc10c5a4c67","analyzedAt":"2026-08-16T02:51:03.250Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}