Debugging an Android-Only FormData Upload Failure

TIL an upload that worked reliably on iOS and failed intermittently on Android came down to a missing file extension on the filename I was generating — Android's multipart parsing cared, iOS's didn't.

2 min read

Sabin Shrestha

Full-Stack Developer — Next.js, React & React Native

The Problem #

The delivery-photo upload worked reliably on iOS test devices and failed intermittently on Android — not every time, and not with a consistent error, which made it far more annoying to pin down than a hard failure would have been.

Context #

The name field in the FormData file object was generated dynamically from a timestamp, since photos didn't need human-readable names, just uniqueness: photo-${Date.now()}.

What I Tried #

Assumed it was the same class of network flakiness I'd seen elsewhere and wrapped the upload in retry logic.

What Went Wrong #

Retries "fixed" it in the sense that eventually one attempt would succeed, which masked the actual bug for longer than it should have — the failure was deterministic given the same filename, not genuinely random.

The Solution #

Logged the exact filename on every failed attempt and noticed the pattern: my generated name (photo-1743600000000) had no file extension. Android's multipart handling on some OkHttp-based stacks uses the filename to infer content type when the explicit type field is ambiguous or stripped by an intermediate layer; without an extension, that inference failed and the server received a body it couldn't parse as an image.

// Before: no extension, worked on iOS, unreliable on Android
const filename = `photo-${Date.now()}`;
 
// After: explicit extension, consistent on both platforms
const filename = `photo-${Date.now()}.jpg`;

Why It Works #

iOS's networking stack and Android's diverge in how strictly they rely on the explicit type field versus inferring content type from the filename extension as a fallback. Giving the file a real extension removes the ambiguity either platform could stumble on, instead of depending on both platforms handling a missing extension identically — which they don't.

Lessons Learned #

"Intermittent" was misleading me — it wasn't random, it was deterministic per-platform and I just hadn't isolated the platform variable yet. Retry logic can accidentally hide a deterministic bug behind what looks like flakiness.

What I Would Do Differently #

I'd log the exact request payload (filename, MIME type, size) on every upload failure from day one, instead of adding that logging only after retries failed to fully resolve the symptom.

MIME type inference from file extensions, platform differences in networking stacks, deterministic vs. flaky failures.