{"record":{"id":"ada76b35fe8e2273","repo":"openclaw/openclaw","slug":"browser-control-is-disabled","errorCode":null,"errorMessage":"Browser control is disabled","messagePattern":"Browser control is disabled","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extensions/browser/src/browser/extension-relay/gateway-relay-route.ts","lineNumber":69,"sourceCode":"function requestedProfileName(resource: string, fallback: string): string {\n  return new URL(resource, \"http://127.0.0.1\").searchParams.get(\"profile\") ?? fallback;\n}\n\nfunction defaultExtensionProfileName(profiles: Record<string, { driver?: string }>): string {\n  for (const [name, profile] of Object.entries(profiles)) {\n    if (profile.driver === \"extension\") {\n      return name;\n    }\n  }\n  return \"chrome\";\n}\n\nasync function resolveGatewayBridge(resource: string) {\n  let state = getBrowserControlState();\n  if (!state) {\n    state = await startBrowserControlServiceFromConfig();\n    if (!state) {\n      throw new Error(\"Browser control is disabled\");\n    }\n  }\n  const profileName = requestedProfileName(\n    resource,\n    defaultExtensionProfileName(state.resolved.profiles),\n  );\n  const resolved = resolveProfile(state.resolved, profileName);\n  if (!resolved || resolved.driver !== \"extension\") {\n    throw new Error(`Extension browser profile \"${profileName}\" was not found`);\n  }\n  return {\n    bridge: (await ensureExtensionRelayForProfile(state, resolved)).bridge,\n    profileName,\n  };\n}\n\n/** Handle the plugin-owned Gateway upgrade path. */\nexport async function handleGatewayExtensionUpgrade(","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/openclaw/openclaw/blob/01804a75319da4b69c9ab98ceaa30477e22b8c0b/extensions/browser/src/browser/extension-relay/gateway-relay-route.ts#L51-L87","documentation":"Thrown by resolveGatewayBridge when an extension-relay Gateway upgrade arrives but Browser control cannot be obtained: getBrowserControlState() is null and startBrowserControlServiceFromConfig() also returns null. A null return means the bundled Browser plugin is effectively disabled (config browser.enabled=false, plugins.browser disabled, or resolved.enabled=false) or browser auth could not be auto-configured. This guards the WebSocket upgrade path so a connecting Chrome extension gets a deterministic failure instead of a half-bound relay.","triggerScenarios":"Occurs during handleGatewayExtensionUpgrade (the /browser/extension WebSocket path) when: (a) openclaw.json sets browser.enabled=false; (b) plugins.browser.enabled=false; (c) the Browser plugin is disabled by normalizePluginsConfig; or (d) resolveBrowserConfig yields enabled=false. The check runs after origin and token validation succeed, inside prepareAuthenticated (v2) or the legacy resolveGatewayBridge call.","commonSituations":"Operators who explicitly disabled the browser plugin but still have the Chrome extension installed and attempting to connect; environments where a prior plugin was disabled via plugins config; CI/test harnesses that load a stripped config; configs where browser.enabled was set false during a security lockdown without removing the extension pairing.","solutions":["Verify openclaw.json does not set browser.enabled=false and that plugins.browser is not disabled; run `openclaw configure` or check `openclaw doctor`.","Confirm the bundled browser plugin is enabled (enabledByDefault is true) and no plugins.browser.enabled=false override exists.","If browser control is intentionally disabled, uninstall or disconnect the Chrome extension so it stops attempting the relay upgrade.","Re-run `openclaw browser extension pair` after re-enabling to confirm the relay secret and extension handshake work end to end."],"exampleFix":"// before (openclaw.json)\n{\n  \"browser\": { \"enabled\": false }\n}\n// after\n{\n  \"browser\": { \"enabled\": true }\n}","handlingStrategy":"validation","validationCode":"import { getRuntimeConfig } from '../config/config.js';\nimport { isDefaultBrowserPluginEnabled } from '../plugin-enabled.js';\n\nfunction assertBrowserControlStartable(): void {\n  const cfg = getRuntimeConfig();\n  if (!isDefaultBrowserPluginEnabled(cfg)) {\n    throw new Error('Browser plugin is disabled in config; cannot start Browser control');\n  }\n  if (cfg.browser?.enabled === false) {\n    throw new Error('browser.enabled=false; cannot start Browser control');\n  }\n}\n// Call before issuing extension-relay upgrade or local dispatch.","typeGuard":null,"tryCatchPattern":"try {\n  const { bridge, profileName } = await resolveGatewayBridge(resource);\n  // ...\n} catch (err) {\n  if (err instanceof Error && err.message === 'Browser control is disabled') {\n    destroy(socket, '503 Service Unavailable'); // surface to extension as transient\n    return;\n  }\n  throw err;\n}","preventionTips":["Validate browser plugin enablement in onboarding/startup before accepting extension upgrades.","Expose a doctor check that warns when the extension is paired but the plugin is disabled.","Treat 'Browser control is disabled' as a config-state error, not a transient one — do not retry without a config change."],"tags":["browser","config","extension-relay","gateway","plugin-enabled"],"backgroundTag":null,"analyzedSha":"01804a75319da4b69c9ab98ceaa30477e22b8c0b","analyzedAt":"2026-08-12T04:37:58.197Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}