{"record":{"id":"849c3454abf5612a","repo":"musistudio/claude-code-router","slug":"only-http-and-https-qr-login-urls-can-be-opened","errorCode":null,"errorMessage":"Only http and https QR login URLs can be opened.","messagePattern":"Only http and https QR login URLs can be opened\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/electron/src/main/bot-gateway-qr-window-service.ts","lineNumber":91,"sourceCode":"\nexport function closeBotGatewayQrWindow(\n  request: BotGatewayQrWindowCloseRequest\n): BotGatewayQrWindowCloseResult {\n  const sessionId = request.sessionId.trim();\n  const window = qrWindows.get(sessionId);\n  if (!window || window.isDestroyed()) {\n    qrWindows.delete(sessionId);\n    return { closed: false };\n  }\n  qrWindows.delete(sessionId);\n  window.close();\n  return { closed: true };\n}\n\nfunction parseQrWindowUrl(value: string): string {\n  const trimmed = value.trim();\n  if (!isHttpUrl(trimmed)) {\n    throw new Error(\"Only http and https QR login URLs can be opened.\");\n  }\n  return new URL(trimmed).toString();\n}\n\nasync function loadQrWindowUrl(window: BrowserWindow, url: string, allowClosed: boolean) {\n  try {\n    await window.loadURL(url);\n  } catch (error) {\n    if (allowClosed && window.isDestroyed()) {\n      return;\n    }\n    throw error;\n  }\n}\n\nasync function waitForQrWindowClose(\n  window: BrowserWindow\n): Promise<Omit<BotGatewayQrWindowOpenResult, \"opened\">> {","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/musistudio/claude-code-router/blob/99f24806c6a2c660b16e53e95211c517448a6c90/packages/electron/src/main/bot-gateway-qr-window-service.ts#L73-L109","documentation":"Thrown by parseQrWindowUrl when the URL for a bot-gateway QR login window fails the isHttpUrl check. QR windows are restricted to http/https because they render remote login pages; other schemes could execute privileged BrowserWindow behavior or load local content.","triggerScenarios":"Calling openBotGatewayQrWindow with a url that is not http(s): e.g. 'file:///qr.html', 'about:blank', 'data:text/html,...', or a URL string the isHttpUrl helper rejects (missing protocol, chrome:// scheme).","commonSituations":"Passing a local asset path or a data: URI instead of the gateway's HTTP endpoint; the gateway URL was misconfigured or truncated so the protocol is missing.","solutions":["Use the full http:// or https:// URL of the bot gateway's QR login page.","Check gateway configuration for a truncated or scheme-less base URL and fix it.","Do not point the QR window at local files or embedded data URIs."],"exampleFix":"// before\nawait openBotGatewayQrWindow({ sessionId, url: \"file:///app/qr.html\" });\n\n// after\nawait openBotGatewayQrWindow({ sessionId, url: \"https://gateway.example.com/qr\" });","handlingStrategy":"validation","validationCode":"function isHttpUrl(v: string): boolean { try { const p = new URL(v.trim()).protocol; return p === \"http:\" || p === \"https:\"; } catch { return false; } }\nif (!isHttpUrl(gatewayQrUrl)) throw new Error(\"Gateway QR URL must be http(s)\");","typeGuard":"function isHttpUrl(value: string): value is `http${\"s\" | \"\"}://${string}` { try { const p = new URL(value.trim()).protocol; return p === \"http:\" || p === \"https:\"; } catch { return false; } }","tryCatchPattern":"try { await openBotGatewayQrWindow({ sessionId, url }); } catch (e) { if (e instanceof Error && e.message.includes(\"Only http and https QR login URLs\")) { /* fix gateway URL config */ } throw e; }","preventionTips":["Validate gateway base URL at config load","Default scheme to https when config omits it","Never point QR windows at file: or data: URIs"],"tags":["url-validation","security","protocol-whitelist","qr-window"],"backgroundTag":"url-protocol-not-allowed","analyzedSha":"99f24806c6a2c660b16e53e95211c517448a6c90","analyzedAt":"2026-08-27T04:11:01.184Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}