transloadit/uppy · error

s3: the part numbers must be a number between 1 and 10000.

Error message

s3: the part numbers must be a number between 1 and 10000.

What it means

After parsing the comma-separated partNumbers query parameter, Companion validates every entry parses as an integer via parseInt. Any non-numeric token ('abc', '', 'null') fails and results in HTTP 400. AWS requires part numbers between 1 and 10000, which is the constraint the message refers to.

Source

Thrown at packages/@uppy/companion/src/server/controllers/s3.ts:409

    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) => {
        return getSignedUrl(
          client,
          new UploadPartCommand({
            Bucket: bucket,
            Key: key,
            UploadId: uploadId,
            PartNumber: Number(partNumber),
            Body: '',
          }),

View on GitHub (pinned to 5d4dedd02a)

Solutions

  1. Filter and validate before joining: parts.filter(Number.isInteger).join(',') with parts in the 1..10000 range.
  2. Check for 0-based indexing bugs — part numbers must start at 1.
  3. Log the outgoing query string to spot empty tokens like '1,,2' from sparse arrays.

Example fix

// before
const partNumbers = uploadedParts.map((p) => p.index).join(',') // may contain 0 or gaps

// after
const partNumbers = uploadedParts
  .map((p) => p.index + 1)
  .filter((n) => Number.isInteger(n) && n >= 1 && n <= 10000)
  .join(',')
Defensive patterns

Strategy: type-guard

Validate before calling

const validParts = parts.filter(
  (n): n is number => Number.isInteger(n) && n >= 1 && n <= 10000,
)
const partNumbers = validParts.join(',')

Type guard

const allValidParts = (arr: unknown[]): boolean =>
  arr.length > 0 &&
  arr.every((n) => Number.isInteger(n) && n >= 1 && n <= 10000)

Prevention

When it happens

Trigger: Sending ?partNumbers=1,2,foo or ?partNumbers=1,,2 (empty token), or interpolating undefined/null values into the joined string, e.g. [1, undefined, 3].join(',') producing '1,,3'.

Common situations: Building the part list from sparse arrays or filtered lists without removing holes; 0-based part indexes ('0' parses to 0 which is falsy and fails); string interpolation of unvalidated user input.

Related errors


AI-assisted analysis of transloadit/uppy@5d4dedd02a (2026-08-28). Data as JSON: /api/errors/5fab8d0ab5468642. Report an issue: GitHub.