eclipse-vertx/vert.x · error · IllegalArgumentException
contentLength must be >= 0
Error message
contentLength must be >= 0
What it means
HttpUtils.positiveLongToString(long) converts non-negative longs to String, caching the first 256 values for hot paths (used e.g. for content-length rendering). It throws IllegalArgumentException("contentLength must be >= 0") when given a negative value, because a negative length is meaningless for HTTP message bodies. The message points at its main caller: rendering the Content-Length header of a request or response.
Source
Thrown at vertx-core/src/main/java/io/vertx/core/http/impl/HttpUtils.java:986
*/
public static HostAndPort socketAddressToHostAndPort(SocketAddress socketAddress) {
if (socketAddress instanceof InetSocketAddress) {
InetSocketAddress inetSocketAddress = (InetSocketAddress) socketAddress;
return new HostAndPortImpl(inetSocketAddress.getHostString(), inetSocketAddress.getPort());
}
return null;
}
private static final String[] SMALL_POSITIVE_LONGS = new String[256];
/**
* This try hard to cache the first 256 positive longs as strings [0, 255] to avoid the cost of creating a new
* string for each of them.<br>
* The size/capacity of the cache is subject to change but this method is expected to be used for hot and frequent code paths.
*/
public static String positiveLongToString(long value) {
if (value < 0) {
throw new IllegalArgumentException("contentLength must be >= 0");
}
if (value >= SMALL_POSITIVE_LONGS.length) {
return Long.toString(value);
}
final int index = (int) value;
String str = SMALL_POSITIVE_LONGS[index];
if (str == null) {
// it's ok to be racy here, String is immutable hence it benefits from safe publication!
str = Long.toString(value);
SMALL_POSITIVE_LONGS[index] = str;
}
return str;
}
public static List<String> fromHttpAlpnVersions(List<io.vertx.core.http.HttpVersion> alpnVersions) {
return alpnVersions
.stream()
.map(io.vertx.core.http.HttpVersion::alpnName)View on GitHub (pinned to fb308bd8c3)
Solutions
- Find where the negative length originates (log inputs before the call) and fix the computation or initialization so the value is >= 0.
- Treat -1 as 'unknown length': use chunked transfer encoding (no Content-Length) instead of sending a negative value.
- Check that file/stream size sources (e.g. File#length) are not returning -1 for missing/unreadable resources and handle that case before writing.
- Guard the caller: validate length >= 0 before invoking the send/putHeader path.
Example fix
// before
long len = file.length() - offset; // can be negative
request.putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(len));
// after
long len = file.length() - offset;
if (len < 0) {
request.setChunked(true); // unknown length -> chunked, no Content-Length
} else {
request.putHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(len));
} Defensive patterns
Strategy: validation
Validate before calling
if (contentLength < 0) {
throw new IllegalArgumentException("contentLength must be >= 0, got " + contentLength);
}
// proceed with putHeader(CONTENT_LENGTH, String.valueOf(contentLength)); Type guard
public static boolean isNonNegative(long v) { return v >= 0; }
// if (isNonNegative(len)) { putHeader(...); } else { setChunked(true); } Try / catch
try {
sendRequestWithLength(len);
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("contentLength")) {
log.error("Negative content length computed: {}", len);
// resend chunked or abort with a 500
} else {
throw e;
}
} Prevention
- Never use -1 as an in-flight sentinel for 'unset' length in variables that reach the send path; use OptionalLong or a separate boolean.
- Assert lengths are >= 0 at boundaries (file size reads, buffer size, total - consumed arithmetic).
- Prefer chunked transfer encoding when the body length is unknown instead of a placeholder length.
- Unit-test length computation with edge inputs (empty body, offset >= size).
When it happens
Trigger: Writing an HTTP request/response whose declared body length is negative — e.g. rendering Content-Length from a computed length of -1, passing a negative contentLength into an API that renders the header via positiveLongToString, or computing length via an underflowing subtraction (total - consumed).
Common situations: Initializing a length counter to -1 and forgetting to set it before sending; subtraction producing a negative result; file/stream size APIs returning -1 for missing or unreadable resources; a -1 'unknown length' sentinel leaking into the send path.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- a header name cannot contain non-ASCII character:
- size must be > 0
- keepAliveTimeout must be >= 0
- maxPoolSize must be > 0
- http2MaxPoolSize must be > 0
AI-assisted analysis of eclipse-vertx/vert.x@fb308bd8c3 (2026-09-06).
Data as JSON: /api/errors/e2f5f63151d7614d.
Report an issue: GitHub.