{"record":{"id":"1f06628f5609b9ce","repo":"transloadit/uppy","slug":"s3mini-partnumber-must-be-a-positive-integer","errorCode":null,"errorMessage":"[s3mini] partNumber must be a positive integer","messagePattern":"\\[s3mini\\] partNumber must be a positive integer","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/@uppy/aws-s3/src/s3-client/S3mini.ts","lineNumber":170,"sourceCode":"  }\n\n  private _checkKey(key: string): void {\n    if (typeof key !== 'string' || key.trim().length === 0) {\n      throw new TypeError(C.ERROR_KEY_REQUIRED)\n    }\n  }\n\n  private _validateUploadPartParams(\n    key: string,\n    uploadId: string,\n    partNumber: number,\n  ): void {\n    this._checkKey(key)\n    if (typeof uploadId !== 'string' || uploadId.trim().length === 0) {\n      throw new TypeError(C.ERROR_UPLOAD_ID_REQUIRED)\n    }\n    if (!Number.isInteger(partNumber) || partNumber <= 0) {\n      throw new TypeError(\n        `${C.ERROR_PREFIX}partNumber must be a positive integer`,\n      )\n    }\n  }\n\n  /**\n   * Uploads an object to S3 using XHR for progress tracking.\n   */\n  public override async putObject({\n    key,\n    data,\n    fileType = C.DEFAULT_STREAM_CONTENT_TYPE,\n    onProgress,\n    signal,\n  }: IT.PutObjectParams) {\n    this._checkKey(key)\n\n    const { xhr, url } = await this.request({","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/aws-s3/src/s3-client/S3mini.ts#L152-L188","documentation":"A parameter guard inside _validateUploadPartParams, invoked by uploadPart, requiring `partNumber` to be a positive integer (1–10000 in S3 terms). Part numbers index parts within a multipart upload and S3 rejects zero, negatives, fractional values, and non-numbers at the protocol level; s3mini rejects them client-side with a TypeError.","triggerScenarios":"Calling uploadPart with partNumber = 0 (e.g. from a 0-based loop index), a fractional number (1.5), a numeric string ('1'), NaN, or undefined.","commonSituations":"Using `array.forEach((chunk, i) => uploadPart({ partNumber: i, ... }))` with a 0-based index instead of i+1; parsing part numbers from strings and forgetting Number(); float math producing non-integers.","solutions":["Use 1-based part numbers: `parts.map((chunk, i) => ({ partNumber: i + 1, body: chunk }))`.","Coerce strings via Number.parseInt and validate: `const n = Number.parseInt(partNumber, 10); if (!Number.isInteger(n) || n < 1 || n > 10000) throw ...`."],"exampleFix":"// before\nchunks.forEach((chunk, i) => s3.uploadPart({ key, uploadId, partNumber: i, body: chunk }))\n// after\nchunks.forEach((chunk, i) => s3.uploadPart({ key, uploadId, partNumber: i + 1, body: chunk }))","handlingStrategy":"validation","validationCode":"const partNumber = Number.parseInt(rawPartNumber, 10)\nif (!Number.isInteger(partNumber) || partNumber < 1 || partNumber > 10000) {\n  throw new RangeError(`Invalid partNumber: ${rawPartNumber}`)\n}","typeGuard":"const isValidPartNumber = (n: unknown): n is number =>\n  Number.isInteger(n) && n >= 1 && n <= 10000","tryCatchPattern":null,"preventionTips":["Always derive part numbers as index + 1 in chunk loops.","Never parse part numbers from strings without Number.parseInt validation."],"tags":["aws-s3","multipart-upload","validation","part-number"],"backgroundTag":"invalid-argument-value","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}