DIYgod/RSSHub · critical · Error

Browser Run API not available. This route requires Cloudflar

Error message

Browser Run API not available. This route requires Cloudflare Browser Run which is only available in remote mode. Use `wrangler dev --remote` or deploy to Cloudflare Workers.

What it means

playwright.worker.ts lazily resolves a Cloudflare Browser Run binding via getBrowserBinding(); if no binding has been registered with setBrowserBinding it throws. Browser Run only exists when the Worker runs remotely (Cloudflare's edge) because the API is provided by the platform, not by local wrangler.

Source

Thrown at lib/utils/playwright.worker.ts:17

// Worker-compatible Playwright using Cloudflare Browser Run.
import type { Browser, Page } from '@cloudflare/playwright';
import { launch } from '@cloudflare/playwright';

import logger from './logger';

type GotoOptions = Parameters<Page['goto']>[1];

let browserBinding: any = null;

export const setBrowserBinding = (binding: any) => {
    browserBinding = binding;
};

const getBrowserBinding = () => {
    if (!browserBinding) {
        throw new Error('Browser Run API not available. This route requires Cloudflare Browser Run which is only available in remote mode. Use `wrangler dev --remote` or deploy to Cloudflare Workers.');
    }
    return browserBinding;
};

const launchBrowser = async () => {
    const browser = await launch(getBrowserBinding(), { keep_alive: 60000 });
    const context = await browser.newContext({
        ignoreHTTPSErrors: true,
    });
    return { browser, context };
};

const scheduleClose = (browser: Browser) => {
    setTimeout(() => {
        void browser.close();
    }, 30000);
};

View on GitHub (pinned to bed535e087)

Solutions

  1. Run the Worker remotely: `wrangler dev --remote`.
  2. Deploy to Cloudflare Workers where the Browser Run API is available.
  3. Ensure wrangler.toml/wrangler.jsonc declares the browser binding and that setBrowserBinding(env.BROWSER) is called in fetch.
  4. For local tests, inject a fake binding via setBrowserBinding(fake) before exercising the route.

Example fix

// before
wrangler dev
// after
wrangler dev --remote
Defensive patterns

Strategy: validation

Validate before calling

function isBrowserAvailable(): boolean {
  return typeof browserBinding !== 'undefined' && browserBinding !== null;
}
// before launching
if (!isBrowserAvailable()) {
  return new Response('Run with `wrangler dev --remote` or deploy to Workers', { status: 503 });
}

Type guard

const hasBrowserBinding = (env: unknown): env is { BROWSER: unknown } =>
  !!env && typeof (env as any)?.BROWSER === 'object';

Try / catch

try {
  return await launchBrowser();
} catch (e) {
  if (/Browser Run API not available/.test((e as Error).message)) {
    return new Response('Browser Run requires --remote mode', { status: 503 });
  }
  throw e;
}

Prevention

When it happens

Trigger: A route that imports playwright.worker.ts is invoked while `wrangler dev` is running without `--remote`, or in a unit test that never calls setBrowserBinding, or in a deployed Worker whose wrangler.toml lacks the browser binding.

Common situations: Local development with `wrangler dev` (default is local); forgetting to add `browser: { binding: "BROWSER" }` to wrangler config; running the Worker bundle under miniflare directly without injecting the binding.

Related errors


AI-assisted analysis of DIYgod/RSSHub@bed535e087 (2026-08-12). Data as JSON: /api/errors/d30074d8682162b4. Report an issue: GitHub.