transloadit/uppy · error

s3: the object key must be passed as a query parameter. For

Error message

s3: the object key must be passed as a query parameter. For example: "?key=abc.jpg"

What it means

The GET /s3/multipart/:uploadId endpoint (listing already uploaded parts) requires the S3 object key to be passed as the key query parameter so Companion knows which object the uploadId belongs to. Without a string key, Companion returns HTTP 400 with this message including the expected format ?key=abc.jpg.

Source

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

   *     - PartNumber - the index of this part.
   *     - ETag - a hash of this part's contents, used to refer to it.
   *     - Size - size of this part.
   */
  function getUploadedParts(req: Request, res: Response, next: NextFunction) {
    const client = getS3Client(req, res)
    if (!client) return
    const s3Client = client

    const { uploadId } = req.params
    const { key } = req.query

    assert(
      typeof uploadId === 'string' && uploadId.length > 0,
      's3: uploadId must be provided.',
    )

    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
    }
    const keyStr = key

    const bucket = getBucket({ bucketOrFn: config.bucket, req })

    const parts: Part[] = []

    const listPartsPage = (startAt?: string) => {
      s3Client
        .send(
          new ListPartsCommand({
            Bucket: bucket,
            Key: keyStr,
            UploadId: uploadId,

View on GitHub (pinned to 5d4dedd02a)

Solutions

  1. Append the object key as a query parameter: GET /s3/multipart/${encodeURIComponent(uploadId)}?key=${encodeURIComponent(key)}.
  2. Make sure you only send the key once in the query string (duplicate ?key= values become an array and fail the typeof string check).
  3. Keep the key returned by the create-multipart-upload / getKey step and reuse it verbatim for subsequent part calls.

Example fix

// before
fetch(`${companionUrl}/s3/multipart/${uploadId}`)

// after
fetch(
  `${companionUrl}/s3/multipart/${encodeURIComponent(uploadId)}` +
  `?key=${encodeURIComponent(key)}`,
)
Defensive patterns

Strategy: validation

Validate before calling

const url =
  `${companion}/s3/multipart/${encodeURIComponent(uploadId)}` +
  `?key=${encodeURIComponent(key)}`

Type guard

function hasKeyParam(key: unknown): key is string {
  return typeof key === 'string' && key.length > 0
}

Try / catch

if (res.status === 400) {
  const body = await res.json()
  if (body.error.includes('object key must be passed')) {
    throw new Error(`Missing ?key= for uploadId ${uploadId}`)
  }
}

Prevention

When it happens

Trigger: Calling GET /s3/multipart/<uploadId> without a ?key=... query string, or with key passed in the body or headers instead of the query string, or as an array (?key=a&key=b) which Express parses into a non-string.

Common situations: Custom clients that only pass uploadId in the URL and forget the key; refactors that move the key into a header; duplicate query parameters causing Express to yield an array.

Related errors


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