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

  1. Ensure only one HttpProxyCacheServer instance exists (singleton) and that all players share it, so nothing else holds the cache file open while trimming runs.
  2. 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.
  3. Delete stale zero-byte files in the cache directory at app start before building the HttpProxyCacheServer, so trim never has to 'recreate' them.
  4. 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.
  5. 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

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


AI-assisted analysis of CarGuo/GSYVideoPlayer@e5d74d3aa9 (2026-08-14). Data as JSON: /api/errors/1f89050df5516f4a. Report an issue: GitHub.