{"record":{"id":"9bee560bff3d5d19","repo":"hcengineering/platform","slug":"network-error-error","errorCode":null,"errorMessage":"Network error ${error}","messagePattern":"Network error (.+?)","errorType":"exception","errorClass":"NetworkError","httpStatus":null,"severity":"error","filePath":"foundations/core/packages/api-client/src/storage/client.ts","lineNumber":115,"sourceCode":"      throw new StorageError('Missing response body')\n    }\n    return Readable.from(response.body)\n  }\n\n  async put (objectName: string, stream: Readable | Buffer | string, contentType: string, size?: number): Promise<Blob> {\n    const buffer = await toBuffer(stream)\n    const file = new File([new Uint8Array(buffer)], objectName, { type: contentType })\n    const formData = new FormData()\n    formData.append('file', file)\n    let response\n    try {\n      response = await fetch(this.uploadUrl, {\n        method: 'POST',\n        body: formData,\n        headers: { ...this.headers }\n      })\n    } catch (error: any) {\n      throw new NetworkError(`Network error ${error}`)\n    }\n    if (!response.ok) {\n      throw new StorageError(await response.text())\n    }\n    const result = (await response.json()) as BlobUploadResult[]\n    if (Object.hasOwn(result[0], 'id')) {\n      const fileResult = result[0] as BlobUploadSuccess\n      return {\n        _class: core.class.Blob,\n        _id: fileResult.id as Ref<Blob>,\n        space: core.space.Configuration,\n        modifiedOn: fileResult.metadata.lastModified,\n        modifiedBy: core.account.System,\n        provider: '',\n        contentType: fileResult.metadata.contentType,\n        etag: fileResult.metadata.etag,\n        version: null,\n        size: fileResult.metadata.size","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/core/packages/api-client/src/storage/client.ts#L97-L133","documentation":"StorageClientImpl.put wraps its raw fetch to uploadUrl in try/catch; any exception thrown by fetch itself (DNS failure, connection refused/reset, TLS error, abort) is rethrown as NetworkError(`Network error ${error}`). Note this wraps only transport-level failures — HTTP error statuses are handled separately.","triggerScenarios":"Calling storageClient.put(...) when the upload URL is unreachable: wrong UPLOAD_URL config, upload service down, DNS failure, connection reset mid-upload of large multipart bodies, or TLS certificate problems.","commonSituations":"UPLOAD_URL not set or pointing at localhost in a container; k8s service name typo; oversized uploads hitting proxy body-size limits that kill the connection; transient network blips between services.","solutions":["Verify uploadUrl is reachable: curl -X POST the upload endpoint from the same network.","Check config.UPLOAD_URL resolution in connectStorage (it must be absolute or a path concatenated with the base URL, with :workspace substituted).","Inspect the inner error text in NetworkError message (ECONNREFUSED, ENOTFOUND, etc.) and fix infrastructure accordingly.","Add retry with backoff for transient connection errors around put()."],"exampleFix":"// before\nconst blob = await storage.put(name, stream, contentType)\n// after\nlet blob\nfor (let attempt = 0; attempt < 3; attempt++) {\n  try {\n    blob = await storage.put(name, stream, contentType)\n    break\n  } catch (err) {\n    if (err instanceof NetworkError && attempt < 2) {\n      await new Promise(r => setTimeout(r, 2 ** attempt * 500))\n      continue\n    }\n    throw err\n  }\n}","handlingStrategy":"retry","validationCode":"// Validate upload URL before uploading\nconst u = new URL(uploadUrl)\nif (!/^https?:$/.test(u.protocol)) throw new Error(`Bad upload URL: ${uploadUrl}`)\n// optionally: await fetch(uploadUrl, { method: 'OPTIONS' }) as a reachability probe","typeGuard":"function isNetworkError(e: unknown): e is NetworkError {\n  return e instanceof NetworkError\n}","tryCatchPattern":"try {\n  return await storage.put(name, stream, contentType)\n} catch (e) {\n  if (isNetworkError(e)) {\n    // transient transport failure — retry with backoff\n    await sleep(1000)\n    return storage.put(name, stream, contentType)\n  }\n  throw e\n}","preventionTips":["Confirm UPLOAD_URL resolves correctly for the current environment at startup.","Add retries with exponential backoff around put() for transient network errors.","Keep uploads under proxy body-size limits; chunk large files.","Monitor upload service availability with health checks."],"tags":["network","storage","upload","fetch"],"backgroundTag":"network-request-failed","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}