oracle/graal · error · IllegalArgumentException

malformed input: partial character at end

Error message

malformed input: partial character at end

What it means

When a 3-byte UTF-8 sequence (lead byte 1110xxxx) begins too close to the end of the buffer, byteCount+3 exceeds len and BinaryInput throws IllegalArgumentException('malformed input: partial character at end'). The string payload is truncated in the middle of a multi-byte character.

Source

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

                    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));
                    }
                    tempEncodingCharBuffer[charCount++] = (char) (((c1 & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F));
                    break;
                default:
                    /* 10xx xxxx, 1111 xxxx */
                    throw new IllegalArgumentException("malformed input around byte " + byteCount);
            }
        }
        // The number of chars produced may be less than len
        return new String(tempEncodingCharBuffer, 0, charCount);
    }

    /**

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. On the producer, compute the encoded byte length with the same utfLen loop BinaryOutput.writeUTF uses (1/2/3 bytes per char), not String.length().
  2. Verify the total byte count transferred matches the header written for the string.
  3. Use matching GraalVM versions on both peers so the length encoding (short vs LARGE_STRING_TAG header) agrees.
  4. Discard and rebuild corrupted buffers instead of partial re-reads.
Defensive patterns

Strategy: validation

Try / catch

try {
    String s = in.readUTF();
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().contains("partial character at end")) {
        // truncated 3-byte sequence: re-request the record
    }
}

Prevention

When it happens

Trigger: readUTF on a buffer whose remaining length is 1-2 bytes short of the full 3-byte sequence, i.e. the declared length disagrees with the encoded content.

Common situations: Truncated transfer between the host JVM and libgraal; a length field computed with the wrong character-vs-byte semantics on the producer side; buffer reuse where an old shorter string overwrote a longer one.

Understand the failure class

Related errors


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