GopeedLab/gopeed · error
blob runtime is not available
Error message
blob runtime is not available
What it means
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.
Source
Thrown at pkg/download/extension_runtime_webview.go:53
}
if gopeed.Runtime.WebView != nil {
if err := runtimeObject.Set("webview", newJSWebViewRuntime(vm, gopeed.Runtime.WebView)); err != nil {
return err
}
}
}
if err := gopeedObject.Set("runtime", runtimeObject); err != nil {
return err
}
return vm.Set("gopeed", gopeedObject)
}
func newJSBlobRuntime(vm *goja.Runtime) *goja.Object {
obj := vm.NewObject()
_ = obj.Set("createObjectURL", func(call goja.FunctionCall) goja.Value {
fn, ok := goja.AssertFunction(vm.Get("__gopeed_blob_create_object_url"))
if !ok {
panic(vm.ToValue(fmt.Errorf("blob runtime is not available")))
}
value, err := fn(goja.Undefined(), call.Argument(0), call.Argument(1))
if err != nil {
panic(vm.ToValue(err))
}
return value
})
_ = obj.Set("revokeObjectURL", func(call goja.FunctionCall) goja.Value {
fn, ok := goja.AssertFunction(vm.Get("__gopeed_blob_revoke_object_url"))
if !ok {
panic(vm.ToValue(fmt.Errorf("blob runtime is not available")))
}
if _, err := fn(goja.Undefined(), call.Argument(0)); err != nil {
panic(vm.ToValue(err))
}
return goja.Undefined()
})
return objView on GitHub (pinned to 7b7327ffb3)
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
Example fix
// before
const url = gopeed.runtime.blob.createObjectURL(blob);
// after
if (typeof globalThis.__gopeed_blob_create_object_url !== "function") {
throw new Error("blob runtime is unavailable on this host build");
}
const url = gopeed.runtime.blob.createObjectURL(blob); Defensive patterns
Strategy: type-guard
Validate before calling
if (typeof globalThis.__gopeed_blob_create_object_url !== "function") {
gopeed.logger.warn("blob runtime unavailable; skipping object URL creation");
return;
} Type guard
function blobRuntimeAvailable() {
return typeof globalThis.__gopeed_blob_create_object_url === "function";
} Try / catch
try {
const url = gopeed.runtime.blob.createObjectURL(blob);
} catch (e) {
if (String(e.message ?? e).includes("blob runtime is not available")) {
gopeed.logger.warn("host lacks blob runtime");
} else {
throw e;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
AI-assisted analysis of GopeedLab/gopeed@7b7327ffb3 (2026-08-16).
Data as JSON: /api/errors/b58f165a4d277103.
Report an issue: GitHub.