{"record":{"id":"5107671340e4e40e","repo":"hcengineering/platform","slug":"failed-to-upload-multipart-part","errorCode":null,"errorMessage":"Failed to upload multipart part","messagePattern":"Failed to upload multipart part","errorType":"exception","errorClass":"DatalakeError","httpStatus":null,"severity":"error","filePath":"foundations/server/packages/datalake/src/client.ts","lineNumber":433,"sourceCode":"    data: Readable | Buffer | string\n  ): Promise<MultipartUploadPart> {\n    const path = `/upload/multipart/${workspace}/${encodeURIComponent(objectName)}/part`\n    const url = new URL(concatLink(this.endpoint, path))\n    url.searchParams.set('uploadId', multipart.uploadId)\n    url.searchParams.set('partNumber', partNumber.toString())\n\n    const body = data instanceof Readable ? (Readable.toWeb(data) as ReadableStream) : data\n\n    try {\n      const response = await fetchSafe(ctx, url, {\n        method: 'PUT',\n        body: body as BodyInit,\n        headers: { ...this.headers }\n      })\n      return (await response.json()) as MultipartUploadPart\n    } catch (err: any) {\n      ctx.error('failed to upload multipart part', { workspace, objectName, err })\n      throw new DatalakeError('Failed to upload multipart part')\n    }\n  }\n\n  private async multipartUploadComplete (\n    ctx: MeasureContext,\n    workspace: WorkspaceUuid,\n    objectName: string,\n    multipart: MultipartUpload,\n    parts: MultipartUploadPart[]\n  ): Promise<ObjectMetadata> {\n    const path = `/upload/multipart/${workspace}/${encodeURIComponent(objectName)}/complete`\n    const url = new URL(concatLink(this.endpoint, path))\n    url.searchParams.set('uploadId', multipart.uploadId)\n\n    try {\n      const res = await fetchSafe(ctx, url, {\n        method: 'POST',\n        body: JSON.stringify({ parts }),","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/foundations/server/packages/datalake/src/client.ts#L415-L451","documentation":"DatalakeError thrown by the datalake client when the PUT request that uploads a single part of a multipart upload fails. It wraps any lower-level error (NetworkError, NotFoundError, or non-OK HTTP response from fetchSafe) into one consistent error type; the underlying cause is logged via ctx.error with workspace/objectName. It means the chunk never reached the server or the server rejected the request.","triggerScenarios":"uploadWithMultipart (via put) with a body large enough to be chunked, where the PUT to /upload/multipart/{workspace}/{object}/part fails: fetch rejects (network down, DNS, TLS) or the server returns non-OK (401 bad token, 404 stale uploadId, 500 server error).","commonSituations":"Datalake service restarted mid-upload leaving a stale uploadId; wrong endpoint/auth headers in config; uploading from a stream that errors mid-read; transient network blips on large files.","solutions":["Check the ctx.error log entry for the wrapped err to see the real cause.","Verify the datalake endpoint URL and auth headers (this.endpoint/this.headers) are correct.","Retry the whole uploadWithMultipart call - a fresh uploadId fixes stale-session failures.","Check network connectivity/proxies between client and datalake service."],"exampleFix":"// before\nawait datalake.put(ctx, wsIds, name, hugeStream)\n// after\ntry {\n  await datalake.put(ctx, wsIds, name, hugeStream)\n} catch (err) {\n  if (err instanceof DatalakeError) {\n    console.error('multipart part upload failed, retrying')\n    await datalake.put(ctx, wsIds, name, hugeStream)\n  } else throw err\n}","handlingStrategy":"retry","validationCode":"const isUploadable = (d: unknown): d is Buffer | string | Readable =>\n  Buffer.isBuffer(d) || typeof d === 'string' || d instanceof Readable\nif (!isUploadable(data)) throw new TypeError('body must be Buffer, string, or Readable')","typeGuard":"const isUploadable = (d: unknown): d is Buffer | string | Readable =>\n  Buffer.isBuffer(d) || typeof d === 'string' || d instanceof Readable","tryCatchPattern":"try {\n  await datalake.put(ctx, wsIds, name, data)\n} catch (err) {\n  if (err instanceof DatalakeError) {\n    await sleep(backoff) // root cause is in ctx.error logs\n    await datalake.put(ctx, wsIds, name, data)\n  } else throw err\n}","preventionTips":["Wrap large uploads in retry-with-backoff logic.","Verify datalake endpoint/auth config before starting long uploads.","Prefer Buffer bodies over streams for small payloads to reduce mid-stream failures.","Monitor ctx.error logs for the wrapped root cause."],"tags":["multipart-upload","network","http"],"backgroundTag":"multipart-upload-failed","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}