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 obj

View on GitHub (pinned to 7b7327ffb3)

Solutions

  1. Run extensions only inside engines created by newExtensionEngine/NewEngine, which always call stream.Enable before extension code executes
  2. Upgrade the host gopeed build so stream.js defines __gopeed_blob_create_object_url (present at stream.js:863)
  3. Feature-detect before calling: skip the blob code path when typeof globalThis.__gopeed_blob_create_object_url !== 'function'
  4. 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

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.