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

  1. Serialize the list to a comma-separated string in the query string: ?partNumbers=1,2,3 (use partNumbers.join(',')).
  2. Verify your HTTP client isn't array-encoding the param (partNumbers[]=1 style fails the typeof string check).
  3. 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

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


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