transloadit/uppy · error
s3: the part number must be a number between 1 and 10000.
Error message
s3: the part number must be a number between 1 and 10000.
What it means
When signing a part upload, Companion validates that partNumber is a string that parses as an integer (via parseInt). A missing, non-numeric, or zero-parsing value like 'abc' or '0' fails the check and gets HTTP 400. AWS multipart part numbers must be between 1 and 10000, hence the message.
Source
Thrown at packages/@uppy/companion/src/server/controllers/s3.ts:338
if (!client) return
const uploadId = req.params['uploadId']
const partNumber = req.params['partNumber']
const key = req.query['key']
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 partNumber !== 'string' || !parseInt(partNumber, 10)) {
res.status(400).json({
error: 's3: the part number must be a number between 1 and 10000.',
})
return
}
const bucket = getBucket({ bucketOrFn: config.bucket, req })
getSignedUrl(
client,
new UploadPartCommand({
Bucket: bucket,
Key: key,
UploadId: uploadId,
PartNumber: Number(partNumber),
Body: '',
}),
{ expiresIn: config.expires },
).then((url) => {View on GitHub (pinned to 5d4dedd02a)
Solutions
- Use 1-based integer part numbers: partNumber = Math.floor(offset / chunkSize) + 1 and interpolate it as a plain integer in the URL.
- Validate partNumber client-side: Number.isInteger(n) && n >= 1 && n <= 10000 before requesting a signature.
- Check for off-by-one bugs where the loop starts at index 0 and passes it directly as the part number.
Example fix
// before (0-based index -> part 0 rejected) chunks.forEach((chunk, i) => signPart(uploadId, i, key)) // after (1-based, 1..10000) chunks.forEach((chunk, i) => signPart(uploadId, i + 1, key))
Defensive patterns
Strategy: type-guard
Validate before calling
const partNumber = Math.floor(byteOffset / chunkSize) + 1
if (!Number.isInteger(partNumber) || partNumber < 1 || partNumber > 10000) {
throw new RangeError(`invalid part number ${partNumber}`)
} Type guard
const isValidPartNumber = (n: unknown): n is number => typeof n === 'number' && Number.isInteger(n) && n >= 1 && n <= 10000
Prevention
- Use 1-based indexing for part numbers
- Compute part numbers from offsets with Math.floor(offset/chunkSize)+1, never from array index alone
- Cap chunk count at 10000 parts (increase chunk size for very large files)
When it happens
Trigger: POST /s3/multipart/<uploadId>/<partNumber> with a non-numeric partNumber such as 'part1' or 'abc', with '0' (parseInt returns 0, falsy), or an empty segment; also non-integer values like '1.5' parse to 1 and pass, but strings like '' fail.
Common situations: Custom chunking loops that compute partNumber incorrectly (0-based indexing sending 0), string concatenation bugs, or template URLs where partNumber is undefined producing '/s3/multipart/<id>/undefined'.
Related errors
- s3: the part numbers must be a number between 1 and 10000.
- s3: content type must be a string
- s3: the object key must be passed as a query parameter. For
- s3: uploadId must be provided.
- s3: the part numbers must be passed as a comma separated que
AI-assisted analysis of transloadit/uppy@5d4dedd02a (2026-08-28).
Data as JSON: /api/errors/9a3725d650611c03.
Report an issue: GitHub.