apache/beam · error · IllegalArgumentException

length is negative:

Error message

length is negative: 

What it means

SubstringByteArrayOutputStream.toString validates that the requested length is non-negative before copying bytes out of the buffer. A negative length indicates the caller computed the record size incorrectly (e.g. end position before start position) and the library fails fast with a clear message instead of a cryptic exception from lower-level array copies.

Source

Thrown at sdks/java/core/src/main/java/org/apache/beam/sdk/io/TextSource.java:480

   *
   * <pre>{@code
   * ByteArrayOutputStream out = ...;
   * byte[] buffer = out.toByteArray(); // 1st-copy
   * String s = new String(buffer, offset, length); // 2nd-copy
   * }</pre>
   */
  static class SubstringByteArrayOutputStream extends ByteArrayOutputStream {
    public String toString(int offset, int length, Charset charset) {
      if (offset < 0) {
        throw new IllegalArgumentException("offset is negative: " + offset);
      }
      if (offset > count) {
        throw new IllegalArgumentException(
            "offset exceeds the buffer limit. offset: " + offset + ", limit: " + count);
      }

      if (length < 0) {
        throw new IllegalArgumentException("length is negative: " + length);
      }

      if (offset + length > count) {
        throw new IllegalArgumentException(
            "offset + length exceeds the buffer limit. offset: "
                + offset
                + ", length: "
                + length
                + ", limit: "
                + count);
      }

      return new String(buf, offset, length, charset);
    }
  }

  /**
   * See <a

View on GitHub (pinned to 12126d8942)

Solutions

  1. Check delimiter search results for -1 before computing length; handle not-found explicitly.
  2. Validate length >= 0 in the caller (e.g. Preconditions.checkArgument(length >= 0)).
  3. Fix the ordering of start/end computation so end >= start always holds for a found record.

Example fix

// before
int len = endIdx - startIdx; // endIdx can be -1
String s = buffer.toString(startIdx, len, UTF_8);

// after
checkState(endIdx >= 0, "record delimiter not found");
int len = endIdx - startIdx;
String s = buffer.toString(startIdx, len, UTF_8);
Defensive patterns

Strategy: validation

Validate before calling

checkState(endIdx >= 0, "record delimiter not found");
checkArgument(endIdx - startIdx >= 0, "record length must be non-negative");

Try / catch

try {
  String s = buffer.toString(startIdx, len, UTF_8);
} catch (IllegalArgumentException e) {
  throw new IllegalStateException("invalid record length: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Calling toString(offset, length, charset) with length < 0 — usually when length = end - start and end < start because record delimiters were searched in the wrong order or the delimiter was not found and -1 was used.

Common situations: Custom TextSource subclasses computing record lengths from indexOf results that returned -1; arithmetic on unsigned values stored in signed ints that overflowed; tests exercising invalid ranges.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/57346a86940b39b0. Report an issue: GitHub.