MuntashirAkon/AppManager · error · IOException
Could not read the whole resource (total = %d, read = %d)
Error message
Could not read the whole resource (total = %d, read = %d)
What it means
Thrown inside the read-side ProxyFileDescriptorCallback pipe in StorageManagerCompat.openProxyFileDescriptor when the total bytes read from the callback does not match the size reported by callback.onGetSize() (and totalSize > 0). It signals a truncated or inconsistent remote resource being proxied through a storage file descriptor.
Source
Thrown at app/src/main/java/io/github/muntashirakon/AppManager/compat/StorageManagerCompat.java:99
@NonNull
public static ParcelFileDescriptor openProxyFileDescriptor(int mode, @NonNull ProxyFileDescriptorCallbackCompat callback)
throws IOException, UnsupportedOperationException {
// We cannot use StorageManager#openProxyFileDescriptor directly due to its limitation on how callbacks are handled
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createReliablePipe();
if ((mode & ParcelFileDescriptor.MODE_READ_ONLY) != 0) {
// Reading requested i.e. we have to read from our side and write it to the target
callback.mHandler.post(() -> {
try (ParcelFileDescriptor.AutoCloseOutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(pipe[1])) {
long totalSize = callback.onGetSize();
long currOffset = 0;
byte[] buf = new byte[DEFAULT_BUFFER_SIZE];
int size;
while ((size = callback.onRead(currOffset, DEFAULT_BUFFER_SIZE, buf)) > 0) {
os.write(buf, 0, size);
currOffset += size;
}
if (totalSize > 0 && currOffset != totalSize) {
throw new IOException(String.format(Locale.ROOT, "Could not read the whole resource (total = %d, read = %d)", totalSize, currOffset));
}
} catch (IOException | ErrnoException e) {
Log.e(TAG, "Failed to read file.", e);
try {
pipe[1].closeWithError(e.getMessage());
} catch (IOException exc) {
Log.e(TAG, "Can't even close PFD with error.", exc);
}
} finally {
callback.onRelease();
}
});
return pipe[0];
} else if ((mode & ParcelFileDescriptor.MODE_WRITE_ONLY) != 0) {
// Writing requested i.e. we have to read from the target and write it to our side
callback.mHandler.post(() -> {
try (ParcelFileDescriptor.AutoCloseInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pipe[0])) {
long currOffset = 0;View on GitHub (pinned to 0152f468fc)
Solutions
- Make onGetSize() return the exact byte count of the content actually served (decompressed size)
- Fix the source stream in onRead so it supplies the full totalSize bytes or return <= 0 only at true EOF
- Handle the IOException thrown to the reader via closeWithError and check LocalInterop/pipe error messages
- Verify server sends correct Content-Length and no transfer-encoding mismatch
Example fix
// before
long totalSize = urlConnection.getContentLength(); // may mismatch gzip body
// after
try (InputStream in = urlConnection.getInputStream()) {
long totalSize = countStream(in); // actual decoded byte count
} Defensive patterns
Strategy: validation
Validate before calling
// verify reported size matches actual content before proxying
long actual = countBytes(sourceStream);
if (reportedSize != actual) {
throw new IllegalStateException("Size metadata mismatch: " + reportedSize + " vs " + actual);
} Try / catch
try (ParcelFileDescriptor pfd = StorageManagerCompat.openProxyFileDescriptor(
ParcelFileDescriptor.MODE_READ_ONLY, callback, handler)) {
// consume pfd
} catch (IOException e) {
Log.e(TAG, "Proxy read failed: " + e.getMessage());
fallbackToLocalCopy();
} Prevention
- Keep onGetSize() consistent with bytes actually served
- Use decompressed sizes
- Detect EOF vs error in onRead
- Test with slow/unstable sources
When it happens
Trigger: The callback's onRead returns fewer total bytes than onGetSize() reports — e.g. the underlying stream ends early, network truncation, or onGetSize() returns a stale/incorrect size for a growing or compressed resource.
Common situations: Proxying a cloud file whose size metadata differs from actual content; reading over an unstable network connection; size computed from HTTP Content-Length while body was gzip-encoded or truncated.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- Could not write the whole resource (total = %d, read = %d)
- Couldn't find any writable Obb dir
- Could not backup data
- Mode ${mode} is not supported.
- Unable to open output stream.
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/07754e8935de7521.
Report an issue: GitHub.