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
- 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().
- Verify the total byte count transferred matches the header written for the string.
- Use matching GraalVM versions on both peers so the length encoding (short vs LARGE_STRING_TAG header) agrees.
- 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
- Compute byte lengths with char-based 1/2/3 rule, never String.length()
- Match header size (short vs LARGE_STRING_TAG) on both sides
- Discard corrupted buffers instead of partial re-reads
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Partial character at end
- malformed input around byte %d
- String too long to encode, %s bytes
- Unknown tag %d
- Len must be non negative but was %d
AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14).
Data as JSON: /api/errors/d450c3dfd769d436.
Report an issue: GitHub.