google/ExoPlayer · error · NumberFormatException
Invalid UTF-8 sequence first byte: {value}
Error message
Invalid UTF-8 sequence first byte: {value} What it means
Thrown by ParsableByteArray.readUtf8CodePointUntilDelimiter / the UTF-8 code-point reader (used by readStringUntil and friends) when the FIRST byte of a character cannot start a valid UTF-8 sequence — the bit scan (j from 7 down) found no leading-zero bit position that yields length 1-4, i.e. the byte is 0xFF or 0xFE (all/leading ones), which are never legal UTF-8 lead bytes. It surfaces as NumberFormatException because the method decodes a numeric code point.
Source
Thrown at library/common/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java:590
* @return Decoded long value
*/
public long readUtf8EncodedLong() {
int length = 0;
long value = data[position];
// find the high most 0 bit
for (int j = 7; j >= 0; j--) {
if ((value & (1 << j)) == 0) {
if (j < 6) {
value &= (1 << j) - 1;
length = 7 - j;
} else if (j == 7) {
length = 1;
}
break;
}
}
if (length == 0) {
throw new NumberFormatException("Invalid UTF-8 sequence first byte: " + value);
}
for (int i = 1; i < length; i++) {
int x = data[position + i];
if ((x & 0xC0) != 0x80) { // if the high most 0 bit not 7th
throw new NumberFormatException("Invalid UTF-8 sequence continuation byte: " + value);
}
value = (value << 6) | (x & 0x3F);
}
position += length;
return value;
}
/**
* Reads a UTF byte order mark (BOM) and returns the UTF {@link Charset} it represents. Returns
* {@code null} without advancing {@link #getPosition() position} if no BOM is found.
*/
@Nullable
public Charset readUtfCharsetFromBom() {View on GitHub (pinned to dd430f7053)
Solutions
- Verify the data source actually delivered decrypted, complete bytes (check Content-Length, response code, and that a DataSourceError/HttpDataSource exception wasn't swallowed)
- Bounds/alignment check: hex-dump bytes around position before readStringUntil and confirm the expected delimiter exists
- Use readStringUntil with a correct delimiter, or read a length-prefixed string, rather than free-running scans into binary regions
- Catch the NumberFormatException per item and skip/drop the malformed cue/section so playback continues
Example fix
// before
String title = data.readStringUntil((byte) '\n'); // hits 0xFF in corrupted ID3 padding
// after
try {
String title = data.readStringUntil((byte) '\n');
} catch (NumberFormatException e) {
Log.w(TAG, "Malformed metadata, skipping", e);
title = "";
} Defensive patterns
Strategy: try-catch
Validate before calling
int first = p.peekUnsignedByte();
if ((first & 0xFE) == 0xFE) { // 0xFF/0xFE can never start UTF-8
// skip this region instead of reading a string
} Try / catch
try { s = p.readStringUntil(delim); } catch (NumberFormatException e) { /* log and skip the malformed entry, continue parsing */ } Prevention
- Validate responses (status, length, decryption) before parsing as text
- Prefer length-prefixed string reads over delimiter scans in binary regions
- Keep subtitle/metadata parsing fault-isolated per item
When it happens
Trigger: Reading a string from a buffer whose position lands on binary garbage: 0xFF bytes appear in ID3 padding gone wrong, misaligned subtitle cues, encrypted/scrambled segments being parsed as plaintext, or when getPosition() was advanced incorrectly so a length/metadata binary field is consumed as text.
Common situations: HLS/DASH subtitle and metadata parsers hitting partially downloaded or CORS-corrupted responses; custom DataSource returning uncleared buffers; wrong charset assumption (UTF-16 data read as UTF-8); encrypted HLS segments parsed without the decryption key applied first.
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
- Invalid UTF-8 sequence continuation byte: {value}
- Cannot read frame at position ${lastDecodePosition}
- Invalid index {index}, size is {size}
- Top bit not zero: {result}
- Unsupported charset: %s
AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14).
Data as JSON: /api/errors/c8c260882d5af4b8.
Report an issue: GitHub.