oracle/graal · error · IllegalArgumentException

Partial character at end

Error message

Partial character at end

What it means

BinaryInput.readUTF decodes the internal UTF-8-like stream used to marshal strings between Truffle/libgraal and its peer. When a 2-byte sequence (lead byte 110xxxxx) starts near the end of the buffer and its continuation byte is missing, it throws IllegalArgumentException('Partial character at end'). This mirrors java.io.DataInputStream's modified-UTF8 error semantics.

Source

Thrown at compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/truffle/BinaryInput.java:289

            switch (c1 >> 4) {
                case 0:
                case 1:
                case 2:
                case 3:
                case 4:
                case 5:
                case 6:
                case 7:
                    /* 0xxxxxxx */
                    byteCount++;
                    tempEncodingCharBuffer[charCount++] = (char) c1;
                    break;
                case 12:
                case 13:
                    /* 110x xxxx 10xx xxxx */
                    byteCount += 2;
                    if (byteCount > len) {
                        throw new IllegalArgumentException("Partial character at end");
                    }
                    c2 = tempEncodingByteBuffer[byteCount - 1];
                    if ((c2 & 0xC0) != 0x80) {
                        throw new IllegalArgumentException("malformed input around byte " + byteCount);
                    }
                    tempEncodingCharBuffer[charCount++] = (char) (((c1 & 0x1F) << 6) | (c2 & 0x3F));
                    break;
                case 14:
                    /* 1110 xxxx 10xx xxxx 10xx xxxx */
                    byteCount += 3;
                    if (byteCount > len) {
                        throw new IllegalArgumentException("malformed input: partial character at end");
                    }
                    c2 = tempEncodingByteBuffer[byteCount - 2];
                    c3 = tempEncodingByteBuffer[byteCount - 1];
                    if (((c2 & 0xC0) != 0x80) || ((c3 & 0xC0) != 0x80)) {
                        throw new IllegalArgumentException("malformed input around byte " + (byteCount - 1));
                    }

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Verify producer and consumer use the same libgraal/Truffle marshalling format (same GraalVM version on both sides).
  2. Check that the byte count/header written by BinaryOutput.writeUTF matches what BinaryInput.readUTF consumes.
  3. Reproduce with logging around the read to confirm the buffer length is not truncated by an earlier short read.
  4. If the stream is corrupted, discard and regenerate the marshalled payload rather than retrying reads.
Defensive patterns

Strategy: validation

Validate before calling

// Verify buffer has room for the full sequence before readUTF
// (framing sanity: header length must not exceed remaining bytes)

Try / catch

try {
    String s = in.readUTF();
} catch (IllegalArgumentException e) {
    if ("Partial character at end".equals(e.getMessage())) {
        // truncated payload: request re-transfer of the whole record
    }
}

Prevention

When it happens

Trigger: Reading a STRING-tagged value from a BinaryInput where the byte buffer is truncated mid multi-byte character: a mismatch between the declared payload length and the actual encoded string bytes.

Common situations: Corrupted or partially written marshalled data; a producer/consumer version mismatch changing the wire format; reading from a native memory buffer that was freed or resized mid-transfer.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/047ac355ac9544d7. Report an issue: GitHub.