CarGuo/GSYVideoPlayer · warning · ProxyCacheException

Error reading %d bytes with offset %d from file[%d bytes] to

Error message

Error reading %d bytes with offset %d from file[%d bytes] to buffer[%d bytes]

What it means

FileCache.append refuses to write once isCompleted() is true (file renamed from .download to final, opened read-only). Throwing here guards the invariant that a completed cache file is immutable; it fires when the source reader tries to add bytes after completion was already signaled - usually a race or a double-pump in the caching loop.

Source

Thrown at gsyVideoPlayer-proxy_cache/src/main/java/com/danikula/videocache/file/FileCache.java:59

    }

    @Override
    public synchronized long available() throws ProxyCacheException {
        try {
            return (int) dataFile.length();
        } catch (IOException e) {
            throw new ProxyCacheException("Error reading length of file " + file, e);
        }
    }

    @Override
    public synchronized int read(byte[] buffer, long offset, int length) throws ProxyCacheException {
        try {
            dataFile.seek(offset);
            return dataFile.read(buffer, 0, length);
        } catch (IOException e) {
            String format = "Error reading %d bytes with offset %d from file[%d bytes] to buffer[%d bytes]";
            throw new ProxyCacheException(String.format(format, length, offset, available(), buffer.length), e);
        }
    }

    @Override
    public synchronized void append(byte[] data, int length) throws ProxyCacheException {
        try {
            if (isCompleted()) {
                throw new ProxyCacheException("Error append cache: cache file " + file + " is completed!");
            }
            dataFile.seek(available());
            dataFile.write(data, 0, length);
        } catch (IOException e) {
            String format = "Error writing %d bytes to %s from buffer with size %d";
            throw new ProxyCacheException(String.format(format, length, dataFile, data.length), e);
        }
    }

    @Override

View on GitHub (pinned to e5d74d3aa9)

Solutions

  1. Reuse one HttpProxyCacheServer and one player per url; avoid starting two concurrent playbacks of the same url during a retry storm
  2. On this error, simply discard it in logs - the file is already fully cached and usable
  3. If it recurs in your fork, add a state check (isCompleted) before entering the append path in your reader loop

Example fix

// before - unconditional append
fileCache.append(data, read);

// after - guard
if (!fileCache.isCompleted()) {
    fileCache.append(data, read);
}
Defensive patterns

Strategy: type-guard

Validate before calling

if (!fileCache.isCompleted()) {
    fileCache.append(data, read);
}

Type guard

boolean canAppend = !fileCache.isCompleted(); // guard before append

Try / catch

catch (ProxyCacheException e) {
    if (String.valueOf(e.getMessage()).contains("is completed")) {
        // benign: file already fully cached, drop the write
        Log.d(TAG, "append after completion ignored");
    } else throw e;
}

Prevention

When it happens

Trigger: Two paths append to the same FileCache after complete() ran: e.g. ProxyCache.readSource retrying after completion, or two proxy requests for the same url sharing a cache entry where the second writer outlives the first's completion.

Common situations: Concurrent playback of the same url in two players with a shared proxy; race between the completion path (close + rename) and a lingering reader thread after an error/retry cycle. Rare in normal single-player usage.

Related errors


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