{"record":{"id":"6b328579c8fea544","repo":"transloadit/uppy","slug":"s3-the-part-numbers-must-be-passed-as-a-comma-sep","errorCode":null,"errorMessage":"s3: the part numbers must be passed as a comma separated query parameter. For example: \"?partNumbers=4,6,7,21\"","messagePattern":"s3: the part numbers must be passed as a comma separated query parameter\\. For example: \"\\?partNumbers=4,6,7,21\"","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/@uppy/companion/src/server/controllers/s3.ts","lineNumber":400,"sourceCode":"    const uploadId = req.params['uploadId']\n    const key = req.query['key']\n    const partNumbers = req.query['partNumbers']\n\n    if (typeof uploadId !== 'string' || uploadId.length === 0) {\n      res.status(400).json({ error: 's3: uploadId must be provided.' })\n      return\n    }\n\n    if (typeof key !== 'string') {\n      res.status(400).json({\n        error:\n          's3: the object key must be passed as a query parameter. For example: \"?key=abc.jpg\"',\n      })\n      return\n    }\n\n    if (typeof partNumbers !== 'string') {\n      res.status(400).json({\n        error:\n          's3: the part numbers must be passed as a comma separated query parameter. For example: \"?partNumbers=4,6,7,21\"',\n      })\n      return\n    }\n\n    const partNumbersArray = partNumbers.split(',')\n    if (!partNumbersArray.every((partNumber) => parseInt(partNumber, 10))) {\n      res.status(400).json({\n        error: 's3: the part numbers must be a number between 1 and 10000.',\n      })\n      return\n    }\n\n    const bucket = getBucket({ bucketOrFn: config.bucket, req })\n\n    Promise.all(\n      partNumbersArray.map((partNumber) => {","sourceCodeStart":382,"sourceCodeEnd":418,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/companion/src/server/controllers/s3.ts#L382-L418","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nfetch(url, {\n  method: 'POST',\n  body: JSON.stringify({ partNumbers: [1, 2, 3] }),\n})\n\n// after\nconst parts = [1, 2, 3].join(',')\nfetch(`${url}?partNumbers=${parts}&key=${encodeURIComponent(key)}`, {\n  method: 'POST',\n})","handlingStrategy":"validation","validationCode":"const partNumbers = parts.filter((n) => Number.isInteger(n) && n >= 1).join(',')\nif (!partNumbers) throw new Error('no valid part numbers to sign')\nconst url = `${base}?partNumbers=${partNumbers}&key=${encodeURIComponent(key)}`","typeGuard":"const isCommaParts = (s: unknown): s is string =>\n  typeof s === 'string' && s.split(',').every((p) => Number.isInteger(Number(p)) && Number(p) > 0)","tryCatchPattern":"if (res.status === 400 && (await res.json()).error.includes('comma separated')) {\n  // fix serialization (body -> query string) and retry\n}","preventionTips":["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"],"tags":["s3","companion","multipart-upload","batch","query-parameters","request-validation"],"backgroundTag":"missing-query-parameter","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}