A Docs save could not store bytes the platform already held

FixDocsService
Shipped
September 9, 2026 at 8:03 PM UTC
Author
Kamo
Commit
2cdf1ff

Saving a document twice, and renaming one, both ended in "Document cannot be saved, please check your permissions" — and after five retries Docs gave up and discarded the member's work. Neither was a permissions problem. Three separate defects, all reaching the editor as an opaque 500: saveImgContent minted an ImgDat per save. idx_img_dats_hash_unique spans (blake3, sha3-256, size) for the whole platform, so storing content that is already stored is a 23505 — and this path is handed the same bytes routinely: Docs re-sends the byte-identical file after any upload it could not complete, and two saves inside one second ARE byte-identical, because LibreOffice writes dcterms:modified at one-second resolution. That is the whole bug: three saves at 19:31:20/23/24 landed, the fourth 180ms later was identical to the third and 500'd, and the retry loop could then never do anything but repeat it. createNewDocument and ImageService.uploadDocument already find-or-create by hash; this was the one write path that did not. The converted-PDF insert in reconvertDocument had the same shape and is fixed too. saveAsNewDocument passed null client hashes to uploadDocument, which verifies them against its own and throws "Client Blake3 hash does not match server calculation" — null differs, so every Save As has always failed. Renaming lands there too: without SupportsRename/UserCanRename the editor's name field falls through to map.saveAs(), so a rename branched a new document rather than renaming this one. RenameFile is now implemented and advertised. CheckFileInfo returned no LastModifiedTime and PutFile an empty body, so Docs logged "Invalid or missing JSON in WOPI::PutFile HTTP_OK response" and could never record that storage held what it had. Both now carry the ImgDat's timestamp, which moves if and only if the bytes do. And /api/docs/new handed the editor the ORIGINAL. /api/docs/open derives the classic version first precisely so the original is never edited in place; a created document was the one that was. It now derives it too — the reporting member guessed exactly this. PutFile's failure is also logged now. It was swallowed, so the only trace of any of this was an unattributed Hibernate line.

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