{"record":{"id":"f885442b96e0c8ec","repo":"different-ai/openwork","slug":"failed-to-open-browser","errorCode":null,"errorMessage":"Failed to open browser","messagePattern":"Failed to open browser","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/app/src/app/lib/desktop.ts","lineNumber":489,"sourceCode":"  let parsed: URL;\n  try {\n    parsed = new URL(url);\n  } catch {\n    throw new Error(\"Only valid web links can be opened externally.\");\n  }\n  if (parsed.protocol !== \"http:\" && parsed.protocol !== \"https:\") {\n    throw new Error(`External URL protocol \"${parsed.protocol}\" is not allowed.`);\n  }\n  return parsed.toString();\n}\n\nexport async function openDesktopUrl(url: string): Promise<void> {\n  const safeUrl = assertDesktopWebUrl(url);\n  const openExternal = window.__OPENWORK_ELECTRON__?.shell?.openExternal;\n  if (openExternal) {\n    const result = await openExternal(safeUrl);\n    if (result && result.ok === false) {\n      throw new Error(result.error ?? \"Failed to open browser\");\n    }\n    return;\n  }\n  if (typeof window !== \"undefined\") {\n    window.open(safeUrl, \"_blank\", \"noopener,noreferrer\");\n  }\n}\n\nexport async function openDesktopPath(target: string): Promise<void> {\n  const result = await invokeElectronHelper(\"__openPath\", target);\n  if (typeof result === \"string\" && result.trim()) {\n    throw new Error(result);\n  }\n}\n\nexport async function revealDesktopItemInDir(target: string): Promise<void> {\n  const result = await invokeElectronHelper(\"__revealItemInDir\", target);\n  if (typeof result === \"string\" && result.trim()) {","sourceCodeStart":471,"sourceCodeEnd":507,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/app/src/app/lib/desktop.ts#L471-L507","documentation":"openDesktopUrl first validates the URL, then calls the Electron shell bridge's openExternal. If the bridge reports a failure (`result.ok === false`), the error thrown is the bridge's own `result.error`, or the generic 'Failed to open browser' when the bridge supplied no error detail. It signals the OS-level open of the default browser failed, not a URL problem.","triggerScenarios":"`window.__OPENWORK_ELECTRON__.shell.openExternal(safeUrl)` resolving with `{ ok: false }` and no `error` message — e.g. the main process failed to spawn the default browser, no browser is registered, or the OS handler invocation returned an error without a detail string.","commonSituations":"Linux desktop environments without a default browser configured (xdg-open fails); sandboxed/flatpak builds missing portal permissions to launch external apps; a corporate machine with locked-down default app associations; a stale/removed default browser.","solutions":["Set/repair the OS default browser (Settings → Default apps, or `xdg-settings set default-web-browser firefox.desktop` on Linux) and retry.","Use the fallback path by ensuring the bridge is absent only in web contexts — in web builds window.open handles it; if in Electron and it persistently fails, fall back to `window.open(safeUrl, \"_blank\", \"noopener,noreferrer\")` in the renderer.","Check the Electron main process logs around shell.openExternal for the underlying OS error.","Retry after restarting the desktop app if the failure followed a browser uninstall/update while the app was running."],"exampleFix":"// before\nawait openDesktopUrl(link);\n\n// after\ntry {\n  await openDesktopUrl(link);\n} catch (err) {\n  console.error(\"openExternal failed, falling back\", err);\n  window.open(link, \"_blank\", \"noopener,noreferrer\");\n}","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await openDesktopUrl(url);\n} catch (err) {\n  console.error(\"openExternal failed\", err);\n  window.open(url, \"_blank\", \"noopener,noreferrer\");\n}","preventionTips":["Ensure the OS has a working default browser configured, especially on Linux/CI machines.","Fall back to window.open in the renderer whenever the Electron bridge open fails.","Watch Electron main-process logs for shell.openExternal errors after browser updates.","On sandboxed (Flatpak/Snap) builds, verify portal permissions for opening external apps."],"tags":["electron","shell-openexternal","desktop-integration","os-integration"],"backgroundTag":"open-external-failed","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}