{"record":{"id":"63bc5e15e593e794","repo":"different-ai/openwork","slug":"only-valid-web-links-can-be-opened-externally","errorCode":null,"errorMessage":"Only valid web links can be opened externally.","messagePattern":"Only valid web links can be opened externally\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/app/src/app/lib/desktop.ts","lineNumber":475,"sourceCode":"): Promise<Response> {\n  if (isLoopbackUrl(input)) {\n    return globalThis.fetch(input, init);\n  }\n  return desktopFetchThroughMain(input, init, {\n    agentContextDiagnosticsDeadlineAtMs: deadlineAtMs,\n  });\n}\n\n// ---------------------------------------------------------------------------\n// Convenience wrappers\n// ---------------------------------------------------------------------------\n\nexport function assertDesktopWebUrl(url: string): string {\n  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\") {","sourceCodeStart":457,"sourceCodeEnd":493,"githubUrl":"https://github.com/different-ai/openwork/blob/2b7df46e8ae1517d64c896c7793d2d52ec845669/apps/app/src/app/lib/desktop.ts#L457-L493","documentation":"assertDesktopWebUrl validates a URL before it is handed to the OS shell opener. If `new URL(url)` throws — meaning the string is not a parseable absolute URL — the function refuses to proceed with the generic message 'Only valid web links can be opened externally.' This prevents garbage or relative strings from reaching openExternal/window.open.","triggerScenarios":"Calling openDesktopUrl, safeUrl, or waitForManagedMcpAuthorization with a non-URL string: an empty string, a relative path like '/settings', a malformed value like 'http//example.com', or user-pasted text that is not a URL.","commonSituations":"A config field (e.g. server URL or MCP authorize URL) left blank or containing a typo; reading a link from storage or an API response where it was stored relative instead of absolute; user input from a form passed straight to the opener without trimming/normalizing; locale-mangled copy-paste breaking the scheme.","solutions":["Ensure the input is an absolute URL including scheme (https://...) before passing it in; prepend a base when the source is relative (`new URL(rel, location.origin).toString()`).","Trim whitespace and strip stray characters (quotes, trailing punctuation) from user-supplied or copy-pasted links.","Validate the string with a quick `URL.canParse(url)` (or try/catch around `new URL`) in form validation so the user fixes it before the open attempt.","Check where the URL originates (config file, env var, API field) and fix the producer to emit full web URLs."],"exampleFix":"// before\nawait openDesktopUrl(userInput);\n\n// after\nconst target = userInput.startsWith(\"http\") ? userInput : `https://${userInput}`;\nif (!URL.canParse(target)) throw new Error(\"Please enter a valid link\");\nawait openDesktopUrl(target);","handlingStrategy":"validation","validationCode":"function isOpenableWebUrl(url: string): boolean {\n  try { const u = new URL(url); return u.protocol === \"http:\" || u.protocol === \"https:\"; } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await openDesktopUrl(link);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"Only valid web links\")) {\n    showToast(\"That is not a valid link\");\n  } else { throw err; }\n}","preventionTips":["Validate URLs at input boundaries (forms, configs) before they reach open helpers.","Store absolute URLs with schemes in configs/API payloads, never relative paths.","Use URL.canParse() for cheap pre-checks in modern runtimes.","Trim and sanitize pasted links before use."],"tags":["url-validation","input-validation","desktop-integration"],"backgroundTag":"invalid-url-input","analyzedSha":"2b7df46e8ae1517d64c896c7793d2d52ec845669","analyzedAt":"2026-09-01T07:59:23.713Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}