{"record":{"id":"2beeefc564766934","repo":"transloadit/uppy","slug":"s3-content-type-must-be-a-string","errorCode":null,"errorMessage":"s3: content type must be a string","messagePattern":"s3: content type must be a string","errorType":"http","errorClass":null,"httpStatus":400,"severity":"error","filePath":"packages/@uppy/companion/src/server/controllers/s3.ts","lineNumber":219,"sourceCode":"    )\n\n    const bucket = getBucket({\n      bucketOrFn: config.bucket,\n      req,\n      filename: truncatedFilename,\n      metadata,\n    })\n\n    const key = config.getKey({ req, filename: truncatedFilename, metadata })\n\n    if (typeof key !== 'string') {\n      res.status(500).json({\n        error: 's3: filename returned from `getKey` must be a string',\n      })\n      return\n    }\n    if (typeof type !== 'string') {\n      res.status(400).json({ error: 's3: content type must be a string' })\n      return\n    }\n\n    const params = {\n      Bucket: bucket,\n      Key: key,\n      ContentType: type,\n      Metadata: rfc2047EncodeMetadata(metadata),\n      ...(config.acl != null && { ACL: config.acl }),\n      ...(config.awsSse != null && { ServerSideEncryption: config.awsSse }),\n      ...(config.awsSseKmsKeyId != null && {\n        SSEKMSKeyId: config.awsSseKmsKeyId,\n      }),\n    }\n\n    client.send(new CreateMultipartUploadCommand(params)).then((data) => {\n      res.json({\n        key: data.Key,","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/transloadit/uppy/blob/5d4dedd02a1ac0ae022c75c54aca76558f88e256/packages/@uppy/companion/src/server/controllers/s3.ts#L201-L237","documentation":"When initiating an S3 multipart upload, Companion requires the uploaded file's MIME type to be a string before passing it as ContentType to S3's CreateMultipartUpload. If the type field in the request body is missing or not a string, it rejects with HTTP 400. This prevents creating S3 objects with an invalid content type.","triggerScenarios":"POST /s3/multipart with a JSON body that omits the type field, sets it to null, or sends a non-string value; typically caused by an uploader client that doesn't set the file's type before requesting multipart upload credentials.","commonSituations":"Custom uploader implementations or older @uppy/aws-s3 client versions that don't include the file MIME type in the multipart params; files whose type is an empty string or undefined in the browser (common for files without a known extension).","solutions":["Ensure the client sends the file's MIME type in the request body, e.g. { filename, type: file.type } — use file.type || 'application/octet-stream' as a fallback since some files have an empty type.","Upgrade @uppy/aws-s3 and @uppy/companion to matching versions so the request contract matches.","If you're calling the endpoint manually, include a string type in the JSON body."],"exampleFix":"// before (client)\nawait companionClient.post('/s3/multipart', { filename: file.name })\n\n// after\nawait companionClient.post('/s3/multipart', {\n  filename: file.name,\n  type: file.type || 'application/octet-stream',\n})","handlingStrategy":"validation","validationCode":"const type = file.type && typeof file.type === 'string' ? file.type : 'application/octet-stream'\nawait fetch(url, {\n  method: 'POST',\n  headers: { 'Content-Type': 'application/json' },\n  body: JSON.stringify({ filename: file.name, type, metadata }),\n})","typeGuard":"const isNonEmptyString = (v: unknown): v is string =>\n  typeof v === 'string' && v.length > 0","tryCatchPattern":"if (!res.ok) {\n  const { error } = await res.json()\n  if (error.includes('content type must be a string')) {\n    // retry once with type: 'application/octet-stream'\n  }\n}","preventionTips":["Always default file.type to application/octet-stream — browsers leave it empty for unknown extensions","Send type in every multipart-create request body","Keep @uppy/aws-s3 and companion versions in sync"],"tags":["s3","companion","multipart-upload","mime-type","request-validation"],"backgroundTag":"missing-required-request-field","analyzedSha":"5d4dedd02a1ac0ae022c75c54aca76558f88e256","analyzedAt":"2026-08-28T12:18:41.267Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}