fix: Set content type when uploading dispute evidence#4959
fix: Set content type when uploading dispute evidence#4959muditbhutani wants to merge 2 commits into
Conversation
Dispute evidence upload failed with IR_36 ("File content type not
found") whenever the browser reported an empty File.type (common for
csv and unrecognised files). Appending such a File to FormData produces
a multipart part with no Content-Type, which the backend rejects.
Re-wrap the file in a Blob with an explicit content type derived from
the file, falling back to a MIME type inferred from the file extension,
and append it with its filename. Also fix the invalid '.img' accept
filter (now .pdf,.csv,.jpeg,.jpg,.png).
Closes #4833
XyneSpaces
left a comment
There was a problem hiding this comment.
Automated Review Summary
PR: fix: Set content type when uploading dispute evidence
Reviewer: XyneSpaces Automation
Findings
✅ No blocking issues found. This is a well-scoped, surgical bug fix that correctly addresses the IR_36 error when uploading dispute evidence files.
Verification
1. Root Cause Fix (DisputesUtils.res)
- ✅ Correctly adds
getEvidenceFileContentTypehelper with sensible MIME type fallbacks - ✅ Handles the case where
File.typeis empty (common for CSV files) - ✅ Extension-based fallback covers the primary supported formats: pdf, csv, jpeg/jpg, png
- ✅ Returns
application/octet-streamas safe default for unknown types
2. FormData Construction (UploadEvidenceForDisputes.res)
- ✅ Properly uses
appendBlobwith explicit filename (fixes multipart filename issue) - ✅ Creates typed Blob with explicit content type
- ✅ Threading
fileNamethrough toacceptFileis correct
3. File Input Accept Attribute
- ✅ Removes invalid
.imgextension (not a real MIME type) - ✅ Adds missing
.jpgand.pngextensions (icons already exist in UI) - ✅ Result:
.pdf,.csv,.jpeg,.jpg,.png— matches supported MIME types
Code Quality Notes
Positive:
- Clean separation:
DisputesUtils.resfor shared logic, component for UI concerns - Matches the pattern already used in
ReviewDataSummary(CSV upload) - Good inline comment explaining why the fallback is needed
- Minimal, focused changes — only what's needed for the fix
Pattern Consistency:
The getMimeTypeFromFileName function follows the same extension-extraction pattern as getFileTypeFromFileName already in the file. Good reuse of existing logic.
Suggestion (Non-blocking)
Consider adding .gif to both the getMimeTypeFromFileName switch and the accept attribute if GIF images are valid dispute evidence. Currently they're rejected at the file picker level.
Verdict: ✅ Approved — Clean, correct fix for the content type issue. Ready for merge.
XyneSpaces
left a comment
There was a problem hiding this comment.
Code Review: Dispute Evidence Upload Content Type
Following the control-center review checklist:
Tier 1 — High Signal Issues
✅ Reuse Existing Utilities — Pattern matches the existing CSV upload approach in ReviewDataSummary using appendBlob with explicit content type.
✅ Error Handling — Graceful fallback to MIME type inference when browser reports empty File.type.
Tier 2 — Medium Signal Issues
".pdf,.csv,.jpeg,.jpg,.png" is defined inline. Consider extracting this to a shared constant if used elsewhere, or document why .img was removed (it was invalid).
Positive Notes
- Clean separation of concerns with helper functions
getMimeTypeFromFileNameandgetEvidenceFileContentType - Good defensive coding with
application/octet-streamfallback - Accept filter now correctly includes all supported image formats
| key={Int.toString(index)} | ||
| type_="file" | ||
| accept=".pdf,.csv,.img,.jpeg" | ||
| accept=".pdf,.csv,.jpeg,.jpg,.png" |
There was a problem hiding this comment.
Good fix removing .img (invalid extension) and adding .png/.jpg. Consider extracting this to a constant if the same filter is used elsewhere, or add a comment documenting the supported file types for future maintainers.
| switch fileName->getFileTypeFromFileName->String.toLowerCase { | ||
| | "pdf" => "application/pdf" | ||
| | "csv" => "text/csv" | ||
| | "jpeg" | "jpg" => "image/jpeg" |
There was a problem hiding this comment.
Good defensive coding with the isNonEmptyString check before falling back to extension-based MIME type detection. This matches the pattern used in CSV uploads elsewhere.
📋 Review SummaryOverall Assessment: ✅ Approved with minor suggestions Key Findings
What was reviewed
Positive Notes
Reviewed by: Hyperswitch Reviewer Agent |
| afterDotFileType | ||
| } | ||
|
|
||
| @get external getFileMimeType: 'a => string = "type" |
There was a problem hiding this comment.
Since the accept filter constrains uploads to known extensions, deriving the MIME from the filename is always sufficient.
we can drop this @get external getFileMimeType
| // Uploading such a file leaves the multipart part without a Content-Type, which the | ||
| // backend rejects with IR_36 ("File content type not found"). Fall back to a MIME | ||
| // type derived from the file extension so a Content-Type is always present. | ||
| let getEvidenceFileContentType = (fileValue, fileName) => { |
There was a problem hiding this comment.
this too can be dropped.
Type of Change
Description
Uploading dispute evidence failed with:
{ "error": { "type": "invalid_request", "message": "File content type not found", "code": "IR_36" } }Root cause
The evidence file was appended directly to
FormDataviaappend(formData, "file", file). When the browser reports an emptyFile.type(which commonly happens for.csvfiles and files whose type the OS/browser can't infer), the resulting multipart part is sent without aContent-Type. The backend then can't determine the file's content type and rejects the request withIR_36.Fix
Re-wrap the file in a
Blobwith an explicit content type before appending, and append it with its filename viaappendBlob(the same pattern already used for CSV uploads inReviewDataSummary):File.type.pdf,csv,jpeg/jpg,png, elseapplication/octet-stream).This guarantees the multipart
filepart always carries aContent-Type, resolvingIR_36.Also fixed the invalid
accept=".pdf,.csv,.img,.jpeg"filter —.imgis not a real extension and.png/.jpg(which the UI already renders icons for) were missing. Nowaccept=".pdf,.csv,.jpeg,.jpg,.png".Changes
DisputesUtils.res— addgetEvidenceFileContentType(withgetMimeTypeFromFileNamefallback) helper.UploadEvidenceForDisputes.res— build a typedBloband useappendBlobwith the filename; passfileNamethrough toacceptFile; fix theacceptfilter.Motivation and Context
Closes #4833
How did you test it?
npm run re:buildcompiles cleanly with no type errors.new Blob([fileValue], { type: contentType })followed byformData.append("file", fileBlob, fileName), so the multipart part always has a Content-Type..csv/.pdf/image evidence on a dispute → attach succeeds instead of failing with IR_36.Checklist
npm run re:format