{"record":{"id":"0e8f0096344493c3","repo":"OpenHands/OpenHands","slug":"cloud-api-key-or-network-issue","errorCode":null,"errorMessage":"Cloud API key or network issue","messagePattern":"Cloud API key or network issue","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"src/hooks/query/use-backends-health.ts","lineNumber":107,"sourceCode":"    if (backend.authMode !== \"cookie\" && !backend.apiKey?.trim()) {\n      throw new Error(MISSING_BACKEND_API_KEY_ERROR);\n    }\n\n    try {\n      if (backend.authMode === \"cookie\") {\n        await getCloudOrganizations(backend);\n      } else {\n        await getCurrentCloudApiKey(backend);\n      }\n    } catch (error) {\n      if (\n        (axios.isAxiosError(error) && error.response?.status === 401) ||\n        (error instanceof HttpError && error.status === 401)\n      ) {\n        throw new Error(CLOUD_BACKEND_LOGGED_OUT_ERROR);\n      }\n      if (isCorsOrNetworkError(error)) {\n        throw new Error(CLOUD_BACKEND_API_KEY_OR_NETWORK_ERROR);\n      }\n      throw error;\n    }\n    return true;\n  }\n\n  try {\n    const clientOptions = getAgentServerClientOptions({\n      host: backend.host,\n      sessionApiKey: backend.apiKey || null,\n      timeout: PROBE_TIMEOUT_MS,\n    });\n\n    await new SettingsClient(clientOptions).getSettings();\n    const serverInfo = await new ServerClient(clientOptions).getServerInfo();\n    assertAgentServerVersionIsSupported(serverInfo);\n  } catch (error) {\n    if (isSdkHttpStatusError(error, 401)) {","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/OpenHands/OpenHands/blob/500b4c533e9393e44cb92894bcbb18438ef473b6/src/hooks/query/use-backends-health.ts#L89-L125","documentation":"Thrown by the cloud-backend probe when the failure is a CORS or network-level error (not a 401). The probe cannot distinguish a wrong/inactive API key from a network block because both manifest as an opaque browser error with no response body, so they collapse into CLOUD_BACKEND_API_KEY_OR_NETWORK_ERROR. Surfaced through the connectivity dot and the backend's lastError field.","triggerScenarios":"Cloud probe where axios/fetch throws and isCorsOrNetworkError(error) is true (no response.status, ERR_NETWORK, ECONNABORTED, or a CORS-blocked response). Happens with an unreachable cloud host, an invalid API key whose 4xx is swallowed by CORS, a corporate proxy blocking the cloud origin, or a request timeout against PROBE_TIMEOUT_MS (4s).","commonSituations":"Wrong base URL for the cloud backend (typo, missing region prefix); corporate firewall/proxy blocks the cloud host; browser offline; cloud API key with leading/trailing whitespace; CSP/CORS rejection from an embedding host origin; DNS failure for the cloud host.","solutions":["Verify network connectivity to the cloud host (open the host URL in the same browser).","Double-check the cloud backend's host string for typos or a missing region prefix.","If behind a corporate proxy, allowlist the cloud origin.","Regenerate the cloud API key and paste it without surrounding whitespace.","Confirm PROBE_TIMEOUT_MS (4s) is sufficient for the link — increase it if on a slow connection."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// Pre-flight reachability check (best-effort, not authoritative)\nasync function cloudHostReachable(host: string): Promise<boolean> {\n  try {\n    await fetch(`${host}/api/v1/health`, { mode: 'no-cors' });\n    return true;\n  } catch { return false; }\n}","typeGuard":"function isCloudApiKeyOrNetworkError(error: unknown): boolean {\n  return error instanceof Error && error.message === 'Cloud API key or network issue';\n}","tryCatchPattern":"try {\n  await probeBackend(cloudBackend);\n} catch (error) {\n  if (isCloudApiKeyOrNetworkError(error)) {\n    // isRetryableProbeError returns true for this, so probeBackendWithQuickRetry already retried twice\n    // surface a 'check network / key' recovery affordance in the UI\n  } else throw error;\n}","preventionTips":["Validate the cloud host URL format before saving the backend (must be a fully-qualified https URL).","Trim whitespace from API keys on save.","This error is retryable — the hook already retries twice with PROBE_RETRY_DELAY_MS (300ms) before surfacing."],"tags":["cloud","network","cors","backend-health"],"backgroundTag":null,"analyzedSha":"500b4c533e9393e44cb92894bcbb18438ef473b6","analyzedAt":"2026-08-12T10:07:46.034Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}