theonedev/onedev · error · IOException

Skipped only

Error message

Skipped only 

What it means

IOUtils.copyRange copies a byte range from an input stream. It first skips range.getStart() bytes using skip(); if the stream ends (or misbehaves) before all required bytes are skipped, it throws IOException("Skipped only N bytes out of M required.") because the requested range is beyond the available data.

Source

Thrown at server-core/src/main/java/io/onedev/server/util/IOUtils.java:22

import java.io.InputStream;
import java.io.OutputStream;

@SuppressWarnings("deprecation")
public class IOUtils extends org.apache.commons.io.IOUtils {

	public static final int BUFFER_SIZE = 64*1024;

	public static void copyRange(InputStream in, OutputStream out, LongRange range) throws IOException {
		int totalSkipped = 0;
		while (totalSkipped < range.getStart())	 {
			long skipped = in.skip(range.getStart()-totalSkipped);
			if (skipped == 0)
				break;
			totalSkipped += skipped;
		}
		
		if (totalSkipped < range.getStart()) 
			throw new IOException("Skipped only " + totalSkipped + " bytes out of " + range.getStart() + " required.");

		long bytesToCopy = range.getEnd() - range.getStart() + 1;

		byte buffer[] = new byte[BUFFER_SIZE];
		while (bytesToCopy > 0) {
			int bytesRead = in.read(buffer);
			if (bytesRead <= 0) {
				break;
			} else if (bytesRead <= bytesToCopy) {
				out.write(buffer, 0, bytesRead);
				bytesToCopy -= bytesRead;
			} else {
				out.write(buffer, 0, (int) bytesToCopy);
				bytesToCopy = 0;
			}
		}
	}

View on GitHub (pinned to d44925c47c)

Solutions

  1. Verify the resource size before requesting the range; clamp range.getEnd() to size-1 or return 416 Range Not Satisfiable.
  2. Re-read the resource to get a fresh, complete stream if it may have changed concurrently.
  3. Check the source (file/blob) wasn't truncated — compare actual length to expected Content-Length.
  4. Handle the IOException upstream by translating it into an HTTP 416 or an application-level out-of-range error.

Example fix

// before
IOUtils.copyRange(in, new ByteRange(1000, 2000)); // stream only has 50 bytes -> IOException
// after
long size = getResourceSize();
if (range.getEnd() < size) {
  IOUtils.copyRange(in, range);
} else {
  throw newweb.exception... // respond 416 or serve full content
}
Defensive patterns

Strategy: validation

Validate before calling

long size = resource.getSize();
if (range.getStart() >= size) {
    throw new HttpRequestException(HttpStatus.CODE_REQUESTED_RANGE_NOT_SATISFIABLE, "Range not satisfiable");
}

Try / catch

try {
    IOUtils.copyRange(in, range);
} catch (IOException e) {
    if (e.getMessage().startsWith("Skipped only")) {
        // translate to HTTP 416 Range Not Satisfiable
    }
}

Prevention

When it happens

Trigger: copyRange(in, range) where the underlying stream is shorter than range.getStart() bytes — e.g. requesting bytes 100-200 of a 50-byte resource. Also occurs when skip() silently stops early on certain stream implementations.

Common situations: HTTP Range requests against resources whose size changed (file truncated/overwritten between size check and read), cached Content-Length mismatch, or sparse reads at end-of-file.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of theonedev/onedev@d44925c47c (2026-09-06). Data as JSON: /api/errors/e7c2ce9f64bb6989. Report an issue: GitHub.