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.

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.

Appendix: 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)