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
- Verify the resource size before requesting the range; clamp range.getEnd() to size-1 or return 416 Range Not Satisfiable.
- Re-read the resource to get a fresh, complete stream if it may have changed concurrently.
- Check the source (file/blob) wasn't truncated — compare actual length to expected Content-Length.
- 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
- Always clamp ranges against the current resource size before copying.
- Use a single consistent snapshot of the resource for size checks and reads.
- Detect concurrent truncation via checksum/ETag checks.
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.