transloadit/uppy · error
s3: the part numbers must be passed as a comma separated que
Error message
s3: the part numbers must be passed as a comma separated query parameter. For example: "?partNumbers=4,6,7,21"
What it means
The batch part-signing endpoint expects the list of part numbers as a comma-separated partNumbers query parameter (e.g. ?partNumbers=4,6,7,21). If partNumbers is missing or not a string, Companion returns HTTP 400 with this message showing the expected format.
Source
Thrown at packages/@uppy/companion/src/server/controllers/s3.ts:400
const uploadId = req.params['uploadId']
const key = req.query['key']
const partNumbers = req.query['partNumbers']
if (typeof uploadId !== 'string' || uploadId.length === 0) {
res.status(400).json({ error: 's3: uploadId must be provided.' })
return
}
if (typeof key !== 'string') {
res.status(400).json({
error:
's3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"',
})
return
}
if (typeof partNumbers !== 'string') {
res.status(400).json({
error:
's3: the part numbers must be passed as a comma separated query parameter. For example: "?partNumbers=4,6,7,21"',
})
return
}
const partNumbersArray = partNumbers.split(',')
if (!partNumbersArray.every((partNumber) => parseInt(partNumber, 10))) {
res.status(400).json({
error: 's3: the part numbers must be a number between 1 and 10000.',
})
return
}
const bucket = getBucket({ bucketOrFn: config.bucket, req })
Promise.all(
partNumbersArray.map((partNumber) => {View on GitHub (pinned to 5d4dedd02a)
Solutions
- Serialize the list to a comma-separated string in the query string: ?partNumbers=1,2,3 (use partNumbers.join(',')).
- Verify your HTTP client isn't array-encoding the param (partNumbers[]=1 style fails the typeof string check).
- Pass an empty string is also invalid — only issue batch calls when there is at least one part to sign.
Example fix
// before
fetch(url, {
method: 'POST',
body: JSON.stringify({ partNumbers: [1, 2, 3] }),
})
// after
const parts = [1, 2, 3].join(',')
fetch(`${url}?partNumbers=${parts}&key=${encodeURIComponent(key)}`, {
method: 'POST',
}) Defensive patterns
Strategy: validation
Validate before calling
const partNumbers = parts.filter((n) => Number.isInteger(n) && n >= 1).join(',')
if (!partNumbers) throw new Error('no valid part numbers to sign')
const url = `${base}?partNumbers=${partNumbers}&key=${encodeURIComponent(key)}` Type guard
const isCommaParts = (s: unknown): s is string =>
typeof s === 'string' && s.split(',').every((p) => Number.isInteger(Number(p)) && Number(p) > 0) Try / catch
if (res.status === 400 && (await res.json()).error.includes('comma separated')) {
// fix serialization (body -> query string) and retry
} Prevention
- Send partNumbers as a query string, not a JSON body
- Join with commas yourself; avoid client libraries' array serialization
- Skip batch calls when the part list is empty
When it happens
Trigger: POST /s3/multipart/<uploadId>/batch with partNumbers in the request body instead of the query string, omitted entirely, or sent as repeated params (?partNumbers=1&partNumbers=2) which Express parses into an array rather than a string.
Common situations: Custom clients sending a JSON body { partNumbers: [1,2] } because that feels more natural for batch APIs; array-serialized query params from default serializer behavior in axios/fetch wrappers.
Related errors
- s3: the object key must be passed as a query parameter. For
- s3: the part numbers must be a number between 1 and 10000.
- s3: content type must be a string
- s3: uploadId must be provided.
- s3: the part number must be a number between 1 and 10000.
AI-assisted analysis of transloadit/uppy@5d4dedd02a (2026-08-28).
Data as JSON: /api/errors/6b328579c8fea544.
Report an issue: GitHub.