hcengineering/platform · warning
missing body
Error message
missing body
What it means
handleMultipartUploadPart streams the request body as the part data. If req.readable is false — the body was already consumed, is empty, or the request was not sent with a streaming body — the handler responds 400 'missing body'. The part bytes themselves are the payload, so an unreadable body cannot be processed.
Source
Thrown at services/datalake/pod-datalake/src/handlers/multipart.ts:81
res: Response,
datalake: Datalake
): Promise<void> {
const workspace = req.params.workspace as WorkspaceUuid
const partNumber = req.query.partNumber as string
let uuid: string
let uploadId: string
const upload = req.query.uploadId as string
try {
;({ uuid, uploadId } = parseUploadId(upload))
} catch (err: any) {
res.status(400).send('invalid upload id')
return
}
if (!req.readable) {
res.status(400).send('missing body')
return
}
const { bucket } = await datalake.selectStorage(ctx, workspace)
const part = await bucket.uploadMultipartPart(ctx, uuid, { uploadId }, req, {
size: Number.parseInt(req.headers['content-length'] ?? '0'),
partNumber: Number.parseInt(partNumber)
})
ctx.info('multipart-part', { workspace, uuid, uploadId, partNumber })
res.status(200).json(part)
}
export async function handleMultipartUploadComplete (
ctx: MeasureContext,
req: Request,View on GitHub (pinned to 63e28dc964)
Solutions
- Send the raw part bytes as the request body (do not wrap in JSON) and ensure the stream is not pre-consumed.
- Check no middleware reads req before the handler (e.g. a body parser applied to this route).
- Retry with a fresh request object rather than reusing a drained stream.
Example fix
// before
fetch(url, { method: 'PUT', body: JSON.stringify({ data: part }) })
// after
fetch(url, { method: 'PUT', headers: { 'Content-Type': 'application/octet-stream' }, body: partBuffer }) Defensive patterns
Strategy: validation
Validate before calling
if (part.byteLength === 0) {
throw new Error('multipart part is empty; refusing to upload')
} Type guard
function hasBody(body: unknown): body is BodyInit {
return body !== undefined && body !== null && !(typeof body === 'string' && body.length === 0)
} Try / catch
const res = await fetch(partUrl, { method: 'PUT', body: partStream })
if (res.status === 400 && (await res.text()) === 'missing body') {
throw new Error('request body was empty or already consumed; resend with raw part bytes')
} Prevention
- Send raw binary part data, not JSON-wrapped payloads.
- Never reuse a consumed request/stream object on retry — create a fresh one.
- Ensure no middleware (body parser) runs on the part-upload route.
When it happens
Trigger: Sending the part-upload request with an empty body, with Content-Length 0, or after another middleware/consumer already drained the stream; using a JSON body wrapper instead of raw binary body.
Common situations: HTTP clients that buffer/transform the body; proxies that reject or strip the body on chunked uploads; retry logic re-sending a consumed request object.
Related errors
- missing files
- invalid upload id
- Failed to initialize multipart upload
- Failed to complete multipart upload
- Failed to reject multipart upload
AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29).
Data as JSON: /api/errors/339f8e83e936f981.
Report an issue: GitHub.