{"record":{"id":"e03e5d2d629978ab","repo":"MuntashirAkon/AppManager","slug":"could-not-write-the-whole-resource-total-d-read-d","errorCode":null,"errorMessage":"Could not write the whole resource (total = %d, read = %d)","messagePattern":"Could not write the whole resource \\(total = (.+?), read = (.+?)\\)","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/compat/StorageManagerCompat.java","lineNumber":126,"sourceCode":"                } finally {\n                    callback.onRelease();\n                }\n            });\n            return pipe[0];\n        } else if ((mode & ParcelFileDescriptor.MODE_WRITE_ONLY) != 0) {\n            // Writing requested i.e. we have to read from the target and write it to our side\n            callback.mHandler.post(() -> {\n                try (ParcelFileDescriptor.AutoCloseInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pipe[0])) {\n                    long currOffset = 0;\n                    byte[] buf = new byte[DEFAULT_BUFFER_SIZE];\n                    int size;\n                    while ((size = is.read(buf)) != -1) {\n                        callback.onWrite(currOffset, size, buf);\n                        currOffset += size;\n                    }\n                    long totalSize = callback.onGetSize();\n                    if (totalSize > 0 && currOffset != totalSize) {\n                        throw new IOException(String.format(Locale.ROOT, \"Could not write the whole resource (total = %d, read = %d)\", totalSize, currOffset));\n                    }\n                } catch (IOException | ErrnoException e) {\n                    Log.e(TAG, \"Failed to write file.\", e);\n                    try {\n                        pipe[0].closeWithError(e.getMessage());\n                    } catch (IOException exc) {\n                        Log.e(TAG, \"Can't even close PFD with error.\", exc);\n                    }\n                } finally {\n                    callback.onRelease();\n                }\n            });\n            return pipe[1];\n        } else {\n            // Should never happen.\n            pipe[0].close();\n            pipe[1].close();\n            Log.e(TAG, \"Mode \" + mode + \" is not supported.\");","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/compat/StorageManagerCompat.java#L108-L144","documentation":"Thrown inside the write-side pipe in StorageManagerCompat.openProxyFileDescriptor when the bytes written through the FileDescriptor (currOffset) do not equal the size returned by callback.onGetSize(). It indicates the consumer wrote fewer bytes than the resource is supposed to contain.","triggerScenarios":"Writing a proxied file where the writer's stream ends early (input InputStream hit EOF prematurely), onGetSize() reports an inflated size, or an upstream read error silently ended the copy loop.","commonSituations":"Copying a partially downloaded file into a proxied descriptor; size metadata from a DB/cache entry that doesn't match the actual data; interrupted transfers.","solutions":["Ensure onGetSize() returns the real size of the incoming data, or 0/negative if unknown so the check is skipped","Check the source InputStream for early EOF and surface its errors instead of silently ending the loop","Wrap the copy so an IOException on read propagates via closeWithError","Recompute the size after the copy and compare before committing the output"],"exampleFix":"// before\nlong totalSize = callback.onGetSize();\nif (totalSize > 0 && currOffset != totalSize) { throw ... }\n// after\nlong totalSize = callback.onGetSize();\nif (totalSize < 0) totalSize = currOffset; // unknown size: accept what was written\nif (totalSize > 0 && currOffset != totalSize) {\n    throw new IOException(String.format(Locale.ROOT, \"Could not write the whole resource (total = %d, read = %d)\", totalSize, currOffset));\n}","handlingStrategy":"validation","validationCode":"// skip the strict equality check when size is unknown\nlong size = callback.onGetSize();\nif (size <= 0) { /* treat as streaming: accept any byte count */ }","typeGuard":null,"tryCatchPattern":"try {\n    // write through the descriptor\n} catch (IOException e) {\n    long written = getBytesWritten();\n    long expected = callback.onGetSize();\n    Log.e(TAG, \"Wrote \" + written + \"/\" + expected + \" bytes\");\n    restartTransfer();\n}","preventionTips":["Return 0/negative from onGetSize when size unknown","Verify source stream completeness before writing","Persist progress for resumable transfers","Compare checksums after copy"],"tags":["android","storage","file-descriptor","truncated-write"],"backgroundTag":"file-write-failed","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}