Skip to content

fix: Set content type when uploading dispute evidence#4959

Open
muditbhutani wants to merge 2 commits into
mainfrom
fix/dispute-evidence-upload-content-type
Open

fix: Set content type when uploading dispute evidence#4959
muditbhutani wants to merge 2 commits into
mainfrom
fix/dispute-evidence-upload-content-type

Conversation

@muditbhutani

Copy link
Copy Markdown
Collaborator

Type of Change

  • Bug fix (non-breaking change which fixes an issue)

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 FormData via append(formData, "file", file). When the browser reports an empty File.type (which commonly happens for .csv files and files whose type the OS/browser can't infer), the resulting multipart part is sent without a Content-Type. The backend then can't determine the file's content type and rejects the request with IR_36.

Fix

Re-wrap the file in a Blob with an explicit content type before appending, and append it with its filename via appendBlob (the same pattern already used for CSV uploads in ReviewDataSummary):

  • Content type is taken from the file's own File.type.
  • When that is empty, it falls back to a MIME type inferred from the file extension (pdf, csv, jpeg/jpg, png, else application/octet-stream).

This guarantees the multipart file part always carries a Content-Type, resolving IR_36.

Also fixed the invalid accept=".pdf,.csv,.img,.jpeg" filter — .img is not a real extension and .png/.jpg (which the UI already renders icons for) were missing. Now accept=".pdf,.csv,.jpeg,.jpg,.png".

Changes

  • DisputesUtils.res — add getEvidenceFileContentType (with getMimeTypeFromFileName fallback) helper.
  • UploadEvidenceForDisputes.res — build a typed Blob and use appendBlob with the filename; pass fileName through to acceptFile; fix the accept filter.

Motivation and Context

Closes #4833

How did you test it?

  • npm run re:build compiles cleanly with no type errors.
  • Verified generated output appends the file as new Blob([fileValue], { type: contentType }) followed by formData.append("file", fileBlob, fileName), so the multipart part always has a Content-Type.
  • Manual: upload .csv/.pdf/image evidence on a dispute → attach succeeds instead of failing with IR_36.

Checklist

  • I formatted the code npm run re:format
  • My changes generate no new warnings

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
@muditbhutani muditbhutani requested a review from a team as a code owner June 9, 2026 20:42
@semanticdiff-com

Copy link
Copy Markdown

Review changes with  SemanticDiff

@github-actions github-actions Bot added the R-ready-for-review PR is ready for reviewing label Jun 9, 2026

@XyneSpaces XyneSpaces left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 getEvidenceFileContentType helper with sensible MIME type fallbacks
  • ✅ Handles the case where File.type is empty (common for CSV files)
  • ✅ Extension-based fallback covers the primary supported formats: pdf, csv, jpeg/jpg, png
  • ✅ Returns application/octet-stream as safe default for unknown types

2. FormData Construction (UploadEvidenceForDisputes.res)

  • ✅ Properly uses appendBlob with explicit filename (fixes multipart filename issue)
  • ✅ Creates typed Blob with explicit content type
  • ✅ Threading fileName through to acceptFile is correct

3. File Input Accept Attribute

  • ✅ Removes invalid .img extension (not a real MIME type)
  • ✅ Adds missing .jpg and .png extensions (icons already exist in UI)
  • ✅ Result: .pdf,.csv,.jpeg,.jpg,.png — matches supported MIME types

Code Quality Notes

Positive:

  • Clean separation: DisputesUtils.res for 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 XyneSpaces left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

⚠️ Magic Values: The accept filter string ".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 getMimeTypeFromFileName and getEvidenceFileContentType
  • Good defensive coding with application/octet-stream fallback
  • 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"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@XyneSpaces

Copy link
Copy Markdown
Collaborator

📋 Review Summary

Overall Assessment:Approved with minor suggestions

Key Findings

Severity Finding
ℹ️ Info Consider extracting accept filter string to a constant

What was reviewed

  • ✅ Pattern follows existing CSV upload approach
  • ✅ MIME type detection fallback is robust
  • ✅ File type accept filter corrected (removed invalid .img, added .png/.jpg)
  • ✅ Defensive coding with application/octet-stream fallback

Positive Notes

  • Clean separation with helper functions
  • Addresses the root cause (empty File.type for certain extensions)
  • Inline documentation explains the IR_36 error prevention

Reviewed by: Hyperswitch Reviewer Agent

afterDotFileType
}

@get external getFileMimeType: 'a => string = "type"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this too can be dropped.

@github-actions github-actions Bot removed the R-ready-for-review PR is ready for reviewing label Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Dispute evidence upload bug

3 participants