A failed upload must not become a dedup match

Fixkamo-shared-library
Shipped
August 27, 2026 at 5:24 AM UTC
Author
Kamo
Commit
f848f39

A member sent an 11 MB .wav to a chat, got "Cannot invoke ObjectWriteResponse.etag() because response is null", sent it again, and the second one delivered instantly — pointing at bytes that were never stored. Every attempt to play it 500'd with "Object does not exist". Two defects in series. minio-java 8.5.7 has a third outcome for putObject that is neither a result nor an exception: when a MULTIPART upload fails and the compensating abort then SUCCEEDS, S3Base.putMultipartObjectAsync falls out of its catch block and returns the still-null response instead of rethrowing (bytecode offsets 147→171→226→228). Reading its etag threw a NullPointerException, which is not in the per-node catch list — so it escaped the failover loop and the remaining MinIO nodes were never tried. Treat a null write result as a failed node instead: the loop moves on, and if every node fails the caller is told the object was not stored. The row survived it. storeContentAddressed writes the ImgDat with isMissing=true BEFORE the bytes go to storage, which is deliberate — a crash must leave a row that admits it holds nothing. But no dedup lookup checked that flag, so the dead row was a perfectly good match, and the streaming path is not @Transactional so nothing rolled it back. Every dedup query now matches published content only, and a caught upload failure discards the row it just wrote. Only multipart uploads can fail this way, so it was invisible for small files and waited for exactly the large ones a member most minds losing.

All changes

Like what you see shipping?

Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.

Start Free ForeverView Pricing