{"record":{"id":"fe049ec5e5076697","repo":"hcengineering/platform","slug":"http-error-res-status","errorCode":null,"errorMessage":"HTTP error ${res.status}","messagePattern":"HTTP error (.+?)","errorType":"http","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"foundations/core/packages/collaborator-client/src/client.ts","lineNumber":85,"sourceCode":"  ) {}\n\n  private async rpc<P, R>(document: CollaborativeDoc, method: string, payload: P): Promise<R> {\n    const workspace = this.workspace\n    const documentId = encodeDocumentId(workspace, document)\n\n    const url = concatLink(this.collaboratorUrl, `/rpc/${encodeURIComponent(documentId)}`)\n\n    const res = await fetch(url, {\n      method: 'POST',\n      headers: {\n        Authorization: 'Bearer ' + this.token,\n        'Content-Type': 'application/json'\n      },\n      body: JSON.stringify({ method, payload })\n    })\n\n    if (!res.ok) {\n      throw new Error('HTTP error ' + res.status)\n    }\n\n    const result = await res.json()\n\n    if (result.error != null) {\n      throw new Error(result.error)\n    }\n\n    return result as R\n  }\n\n  async getMarkup (document: CollaborativeDoc, source?: Ref<Blob> | null): Promise<Markup> {\n    const payload: GetContentRequest = {\n      source: source !== null ? source : undefined\n    }\n\n    const res = await retry(\n      3,","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/core/packages/collaborator-client/src/client.ts#L67-L103","documentation":"rpc in the collaborator client performs an HTTP POST to the collaborator service and throws a plain Error('HTTP error <status>') when the response is not ok. The status code is included, but the response body (which usually holds the real reason) is discarded.","triggerScenarios":"Any rpc() call (directly or via res/updateMarkup) where the collaborator endpoint returns 401/403/404/5xx — bad URL, expired token, unknown document, or collaborator service outage.","commonSituations":"Collaborator service URL misconfigured; auth token missing/expired; requesting markup for a document that doesn't exist; collaborator service restarting or crashed.","solutions":["Note the status in the message: 401/403 -> fix token; 404 -> check document/URL; 5xx -> retry with backoff.","Verify the collaborator service URL in configuration.","Refresh the auth token before the rpc call.","Improve the error by reading res.text() before throwing, to capture the server's reason.","Catch and map statuses to user-facing messages in the calling layer."],"exampleFix":"// before\nthrow new Error('HTTP error ' + res.status)\n// after\nconst body = await res.text()\nthrow new Error(`HTTP error ${res.status}: ${body}`)","handlingStrategy":"retry","validationCode":"// preflight connectivity\nconst ping = await fetch(collaboratorUrl, { method: 'HEAD' }).catch(() => null)\nif (ping === null || !ping.ok) throw new Error('Collaborator service unreachable')","typeGuard":"function isHttpError(err: unknown): err is Error & { status?: number } {\n  return err instanceof Error && /^HTTP error \\d+$/.test(err.message)\n}","tryCatchPattern":"for (let attempt = 0; attempt < 3; attempt++) {\n  try {\n    return await client.rpc(method, payload)\n  } catch (err) {\n    const status = Number(err.message.split(' ').pop())\n    if (status >= 500 && attempt < 2) { await sleep(2 ** attempt * 500); continue }\n    throw err\n  }\n}","preventionTips":["Verify the collaborator URL in configuration before deploying","Refresh tokens proactively for long-lived sessions","Retry only 5xx statuses; surface 4xx to the user","Include response body in errors for diagnosability"],"tags":["http","collaborator","rpc"],"backgroundTag":"http-non-ok-response","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}