hcengineering/platform · error
Failed to parse response for part ${partNumber}
Error message
Failed to parse response for part ${partNumber} What it means
After a 5MB part PUT via uploadXhr, multipartUploadPart JSON-parses result.responseText to extract the etag. If parsing fails (or the parsed body lacks an etag structure), it throws 'Failed to parse response for part N'. This happens even though uploadXhr only resolves on 2xx, meaning the server returned a 2xx with a non-JSON body.
Source
Thrown at foundations/core/packages/storage-client/src/upload.ts:242
const url = new URL(concatLink(baseUrl, '/part'))
url.searchParams.set('uploadId', uploadId)
url.searchParams.set('partNumber', `${partNumber}`)
const result = await uploadXhr(
{
url: url.toString(),
method: 'PUT',
headers,
body: blob
},
options
)
try {
const response = JSON.parse(result.responseText)
return { etag: response.etag }
} catch (err) {
throw new Error(`Failed to parse response for part ${partNumber}`)
}
}
async function multipartUploadAbort (baseUrl: string, headers: Record<string, string>, uploadId: string): Promise<void> {
const url = new URL(concatLink(baseUrl, '/abort'))
url.searchParams.set('uploadId', uploadId)
const response = await fetch(url, {
method: 'POST',
headers
})
if (!response.ok) {
throw new Error('Failed to reject multipart upload')
}
}
View on GitHub (pinned to 63e28dc964)
Solutions
- Log result.status and result.responseText at the throw site to see what the server actually returned for that part.
- Check intermediate proxies/CDNs for HTML or empty 2xx responses on /part and bypass them for uploads.
- Update the storage service so /part returns JSON {etag: "..."} on success.
- Retry the failed part: multipart part uploads are idempotent for the same partNumber.
Example fix
// before: opaque error, no diagnostic
throw new Error(`Failed to parse response for part ${partNumber}`)
// after: surface what came back
throw new Error(`Failed to parse response for part ${partNumber}: status=${result.status} body=${result.responseText.slice(0, 200)}`) Defensive patterns
Strategy: retry
Validate before calling
// inspect the XHR result before trusting JSON.parse
function looksLikeEtagResponse (r: { status: number, responseText: string }): boolean {
return r.status >= 200 && r.status < 300 && r.responseText.trim().startsWith('{') && r.responseText.includes('etag')
} Type guard
function hasEtag (v: unknown): v is { etag: string } {
return typeof v === 'object' && v !== null && typeof (v as any).etag === 'string'
}
const parsed: unknown = JSON.parse(result.responseText)
if (!hasEtag(parsed)) throw new Error('missing etag') Try / catch
for (let attempt = 0; attempt < 3; attempt++) {
try { return await multipartUploadPart(url, headers, uploadId, partNumber, blob, opts) }
catch (err) {
if (err instanceof Error && err.message.startsWith('Failed to parse response for part')) {
await new Promise(r => setTimeout(r, 2 ** attempt * 500)); continue // part PUTs are idempotent
}
throw err
}
} Prevention
- Bypass HTML-returning proxies/CDNs for upload part routes.
- Confirm the storage service returns JSON {etag} with 2xx on /part.
- Log raw responseText on parse failure for fast diagnosis.
- Retry the specific part — same partNumber PUT is safe to repeat.
When it happens
Trigger: PUT /part?uploadId=...&partNumber=N returns 2xx but the body is not JSON: empty response, an HTML error/maintenance page from a proxy that rewrote the status, a CORS-filtered empty body, or the server omitting the etag field.
Common situations: Nginx/ingress returning HTML 200 pages (custom error or captive portal); CDN caching a non-JSON response for /part; storage service deployed without the etag JSON response (older backend version); dev proxies (Vite/webpack) interfering with XHR responseText.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to initialize multipart upload
- Failed to complete multipart upload
- Network error ${error}
- Failed to reject multipart upload
- missing files
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/89bdb425b38fc54d.
Report an issue: GitHub.