{"record":{"id":"32b07ce8902062f5","repo":"tinyhumansai/openhuman","slug":"transport-cloud-http-response-status-text","errorCode":null,"errorMessage":"[transport:cloud] HTTP ${response.status}: ${text || response.statusText}","messagePattern":"\\[transport:cloud\\] HTTP (.+?): (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/src/services/transport/CloudHttpTransport.ts","lineNumber":75,"sourceCode":"    try {\n      response = await fetch(this.rpcUrl, {\n        method: 'POST',\n        headers,\n        body: JSON.stringify(payload),\n        signal: controller.signal,\n      });\n    } catch (err) {\n      if (controller.signal.aborted) {\n        throw new Error(`[transport:cloud] ${method} timed out after ${this.timeoutMs}ms`);\n      }\n      throw err;\n    } finally {\n      clearTimeout(timeoutId);\n    }\n\n    if (!response.ok) {\n      const text = await response.text();\n      throw new Error(`[transport:cloud] HTTP ${response.status}: ${text || response.statusText}`);\n    }\n\n    const json = (await response.json()) as JsonRpcResponse<T>;\n\n    if (json.error) {\n      logErr('[transport:cloud] ← %s error: %s', method, json.error.message);\n      throw new Error(json.error.message ?? 'Cloud RPC returned an error');\n    }\n    if (!Object.prototype.hasOwnProperty.call(json, 'result')) {\n      throw new Error('[transport:cloud] response missing result');\n    }\n\n    log('[transport:cloud] ← %s id=%d ok', method, id);\n    return json.result as T;\n  }\n\n  async *stream<T>(\n    method: string,","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/tinyhumansai/openhuman/blob/a221052e0df5b1f7598fceba7329fd1af95d6699/app/src/services/transport/CloudHttpTransport.ts#L57-L93","documentation":"Thrown when the cloud core answers with a non-2xx HTTP status: the transport reads the body text and embeds both status and body (falling back to `statusText` when the body is empty). This is transport-level failure before JSON-RPC parsing — the message tells you exactly what the HTTP layer saw.","triggerScenarios":"401 when `bearerToken` is null (the header is simply omitted) or the token from the connection profile is expired/revoked; 404 for a wrong `rpcUrl` path; 405 when hitting a GET-only endpoint; 502/503/504 from the cloud gateway during deploys or outages; 413 for oversized payloads.","commonSituations":"Connection profile created without a token against an auth-required core; stale token after cloud session rotation; typo'd or version-changed rpcUrl; cloud provider incident; reverse-proxy body limits.","solutions":["Read the embedded status: 401/403 → refresh or re-supply the bearer token in the connection profile; 404 → fix rpcUrl; 5xx → retry later","Confirm the rpcUrl includes the full JSON-RPC path, not just the host","For 413, shrink the request (batch or paginate params)","Check the provider status page if 5xx persists across methods"],"exampleFix":"// before\nconst t = new CloudHttpTransport(url, null);\n\n// after — fail fast on config, not mid-call\nif (!profile.bearerToken) throw new Error('Connection profile is missing its cloud token');\nconst t = new CloudHttpTransport(url, profile.bearerToken);","handlingStrategy":"try-catch","validationCode":"const parsed = new URL(rpcUrl);\nif (!/^https?:$/.test(parsed.protocol)) throw new Error('rpcUrl must be http(s)');\nif (requireAuth && !bearerToken) throw new Error('cloud profile missing token');","typeGuard":"function isCloudHttpError(e: unknown): e is Error & { status?: number } {\n  if (!(e instanceof Error) || !e.message.startsWith('[transport:cloud] HTTP ')) return false;\n  return true;\n}","tryCatchPattern":"try { await transport.call(m, p); }\ncatch (e) {\n  const m_ = e instanceof Error ? e.message : '';\n  if (m_.startsWith('[transport:cloud] HTTP 401')) return reAuth();\n  if (/HTTP 5\\d\\d/.test(m_)) return retryLater();\n  throw e;\n}","preventionTips":["Validate the connection profile's URL and token at connect time, not on first call","Parse the embedded status out of the message to branch (401 vs 5xx) instead of showing raw text","Keep bearer tokens fresh — rotate before expiry, since no header is sent when the token is null"],"tags":["http","cloud","auth","transport","network"],"backgroundTag":null,"analyzedSha":"a221052e0df5b1f7598fceba7329fd1af95d6699","analyzedAt":"2026-08-16T12:47:06.542Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}