CarGuo/GSYVideoPlayer · error · IOException
Error recreate zero-size file %s
Error message
Error recreate zero-size file %s
What it means
This IOException is thrown by Files.recreateZeroSizeFile (Files.java:76-80) when the cache file being touched has length 0 and either File.delete() or File.createNewFile() returns false. It is reached from Files.modify() (Files.java:61-66), which the LRU disk-usage logic invokes via Files.setLastModifiedNow() when File.setLastModified() fails on the device (a known Android quirk the code comments on). The library tries to refresh a zero-length cache file's timestamp by deleting and recreating it; if the filesystem refuses either step it throws rather than silently leaving stale LRU ordering.
Source
Thrown at gsyVideoPlayer-proxy_cache/src/main/java/com/danikula/videocache/file/Files.java:78
static void modify(File file) throws IOException {
long size = file.length();
if (size == 0) {
recreateZeroSizeFile(file);
return;
}
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
accessFile.seek(size - 1);
byte lastByte = accessFile.readByte();
accessFile.seek(size - 1);
accessFile.write(lastByte);
accessFile.close();
}
private static void recreateZeroSizeFile(File file) throws IOException {
if (!file.delete() || !file.createNewFile()) {
throw new IOException("Error recreate zero-size file " + file);
}
}
private static final class LastModifiedComparator implements Comparator<File> {
@Override
public int compare(File lhs, File rhs) {
return compareLong(lhs.lastModified(), rhs.lastModified());
}
private int compareLong(long first, long second) {
return Long.compare(first, second);
}
}
}
View on GitHub (pinned to e5d74d3aa9)
Solutions
- Ensure only one HttpProxyCacheServer instance exists (singleton) and that all players share it, so nothing else holds the cache file open while trimming runs.
- Verify the cache directory passed to HttpProxyCacheServer.newCacheRootFactory / cacheRootFactory() is on internal storage (context.getCacheDir()) or is one you provably can write to, and that WRITE_EXTERNAL_STORAGE / scoped-storage rules are satisfied.
- Delete stale zero-byte files in the cache directory at app start before building the HttpProxyCacheServer, so trim never has to 'recreate' them.
- If it persists on specific devices, catch the IOException at the ping/trim call site (e.g. in a custom DiskUsage wrapper or around server building) and log-and-continue: the failure only affects LRU timestamp freshness, not data integrity.
- Check disk space and, if the cache dir lives on removable storage, guard playback start with a mounted-state check (Environment.getExternalStorageState()).
Example fix
// before: cache on external storage with no mount/permission guard
HttpProxyCacheServer proxy = new HttpProxyCacheServer.Builder(context)
.cacheRootFactory(new File(Environment.getExternalStorageDirectory(), "video-cache"))
.build();
// after: cache on internal cache dir (always writable) + prune zero-byte leftovers
File cacheDir = new File(context.getCacheDir(), "video-cache");
if (cacheDir.isDirectory()) {
File[] stale = cacheDir.listFiles();
if (stale != null) {
for (File f : stale) {
if (f.isFile() && f.length() == 0) {
//noinspection ResultOfMethodCallIgnored
f.delete();
}
}
}
}
HttpProxyCacheServer proxy = new HttpProxyCacheServer.Builder(context)
.cacheRootFactory(cacheDir)
.build(); Defensive patterns
Strategy: try-catch
Validate before calling
// Before building the server, ensure the cache dir is writable and has no zero-byte stragglers
File cacheDir = new File(context.getCacheDir(), "video-cache");
boolean usable = cacheDir.isDirectory() || cacheDir.mkdirs();
if (usable) {
File[] files = cacheDir.listFiles();
if (files != null) {
for (File f : files) {
if (f.isFile() && f.length() == 0 && !f.delete()) {
usable = false; // delete failed -> filesystem problem, surface it now
break;
}
}
}
}
if (!usable) throw new IllegalStateException("Video cache dir not writable: " + cacheDir); Try / catch
// Wrap proxy server construction/usage where the IOException can bubble up (it originates
// inside disk-usage trimming and is fatal to that request, not to the app).
try {
HttpProxyCacheServer server = new HttpProxyCacheServer.Builder(context)
.cacheRootFactory(cacheDir)
.build();
} catch (IOException e) {
if (String.valueOf(e.getMessage()).contains("Error recreate zero-size file")) {
// LRU timestamp refresh failed: clear the cache dir and retry once
//noinspection ResultOfMethodCallIgnored
cacheDir.delete();
//noinspection ResultOfMethodCallIgnored
cacheDir.mkdirs();
server = new HttpProxyCacheServer.Builder(context).cacheRootFactory(cacheDir).build();
} else {
throw e;
}
} Prevention
- Use one shared singleton HttpProxyCacheServer so no second process holds cache files open during LRU trimming.
- Prefer context.getCacheDir() (internal storage) for cacheRootFactory; it is always writable and never unmounted.
- Prune zero-length files from the cache directory on app start before creating the proxy server.
- Do not manually delete or lock files inside the proxy's cache directory from other code paths.
- If caching to external storage, check Environment.getExternalStorageState() before starting playback.
When it happens
Trigger: Proxy cache disk trimming runs (HttpProxyCacheServer with an LruDiskUsage), File.setLastModified(now) returns false on the device, the touched cache file's length() == 0, and then file.delete() or file.createNewFile() fails. Typical concrete causes: the file is held open by another thread/process (e.g. a concurrent download writing it), the cache directory was deleted or unmounted between the listFiles() scan and the touch (external storage ejected), or the app lost write access to getExternalCacheDir()/custom cache dir (permissions revoked, disk full).
Common situations: Android devices where setLastModified is broken (comment in Files.java:49 explicitly mentions this, e.g. Nexus 5-class devices); cache directory on external/removable storage that is unmounted while playback runs; a zero-byte .downloaded cache file left over from a previously interrupted/crashed proxy download; two proxy cache servers or a media player plus the trimmer touching the same zero-size file concurrently; runtime storage permission missing after targeting API 30+ scoped storage.
Related errors
- Max count must be positive number!
- Max size must be positive number!
- Exo cache folder is locked: ${cachePath}
- ExoPlayer Cache 未初始化,请先播放视频
- Error reading source ${errorsCount} times
AI-assisted analysis of CarGuo/GSYVideoPlayer@e5d74d3aa9 (2026-08-14).
Data as JSON: /api/errors/1f89050df5516f4a.
Report an issue: GitHub.