{"record":{"id":"3a11192be5f2ca53","repo":"transloadit/uppy","slug":"s3-returned-xhr-status-servicecode-ser","errorCode":null,"errorMessage":"S3 returned ${xhr.status}${serviceCode ? ` – ${serviceCode}` : ''}","messagePattern":"S3 returned (.+?)(.+?)` : ''\\}","errorType":"exception","errorClass":"S3ServiceError","httpStatus":null,"severity":"error","filePath":"packages/@uppy/aws-s3/src/s3-client/S3mini.ts","lineNumber":365,"sourceCode":"          shouldRetryCredentials &&\n          this.getCredentials != null &&\n          serviceCode != null &&\n          ['ExpiredToken', 'InvalidAccessKeyId'].includes(serviceCode)\n        ) {\n          this.clearCachedCredentials()\n\n          // Retry with fresh credentials\n          return this.request({\n            request,\n            data,\n            onProgress,\n            signal,\n            contentType,\n            shouldRetryCredentials: false, // prevent infinite recursion\n          })\n        }\n\n        throw new U.S3ServiceError(\n          `S3 returned ${xhr.status}${serviceCode ? ` – ${serviceCode}` : ''}`,\n          xhr.status,\n          serviceCode,\n          xhr.responseText,\n        )\n      }\n\n      throw err\n    }\n  }\n\n  /** Lists uploaded parts for a multipart upload. */\n  public override async listParts({\n    uploadId,\n    key,\n    signal,\n  }: IT.ListPartsParams): Promise<IT.UploadPart[]> {\n    this._checkKey(key)","sourceCodeStart":347,"sourceCodeEnd":383,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/aws-s3/src/s3-client/S3mini.ts#L347-L383","documentation":"Thrown by S3mini's request() when the S3 endpoint returns a non-2xx HTTP status that isn't a retryable/auth case. It wraps the status code, an optional parsed S3 service error Code, and the response body into a U.S3ServiceError so callers can inspect status and serviceCode.","triggerScenarios":"Any S3 REST call (putObject, listParts, completeMultipartUpload, etc.) where xhr.status is an error: 400 (malformed XML/signature), 403 (bad credentials), 404 (wrong endpoint/bucket), 503 (slow down).","commonSituations":"Misconfigured endpoint URL or region, expired STS credentials, wrong bucket name, SigV4 signing mismatches, S3 rate limiting (503 SlowDown).","solutions":["Inspect error.status and error.serviceCode (e.g. 'AccessDenied', 'NoSuchBucket', 'AuthorizationHeaderMalformed') to identify the cause","Verify the endpoint/region and bucket configuration in the S3 mini client options","Refresh or verify credentials if status is 403","Retry with backoff on 5xx/503 SlowDown statuses"],"exampleFix":"// before\nawait s3Mini.putObject({ key, body })\n\n// after\ntry {\n  await s3Mini.putObject({ key, body })\n} catch (err) {\n  if (err instanceof S3ServiceError && err.status === 503) await retryLater()\n  else throw err\n}","handlingStrategy":"retry","validationCode":"// Validate config before use\nif (!endpoint || !credentials.accessKeyId) throw new Error('S3 client misconfigured')","typeGuard":"function isS3ServiceError(e: unknown): e is S3ServiceError {\n  return e instanceof Error && 'status' in e && 'serviceCode' in e\n}","tryCatchPattern":"try { await s3.call(...) } catch (e) { if (isS3ServiceError(e) && (e.status === 503 || e.status >= 500)) return backoffRetry(); throw e }","preventionTips":["Verify endpoint, region, and bucket before starting uploads","Refresh STS credentials proactively before expiry","Implement exponential backoff for 5xx/503 SlowDown"],"tags":["s3","http-status","aws","multipart"],"backgroundTag":"s3-http-error-response","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}