google/ExoPlayer · error · IllegalArgumentException
Unsupported charset: %s
Error message
Unsupported charset: %s
What it means
findNextLineTerminator (backing readSingleLineWithDelimiter and readSingleLine* methods) only supports UTF-8, US-ASCII, UTF-16, UTF-16LE, and UTF-16BE, because line-terminator scanning needs the charset's code-unit stride (1 or 2 bytes). Passing any other java.nio.charset.Charset — UTF-16 with BOM variants handled upstream, but e.g. UTF-32, ISO-8859-1, or a custom charset — throws IllegalArgumentException immediately.
Source
Thrown at library/common/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java:639
return Charsets.UTF_16LE;
}
}
return null;
}
/**
* Returns the index of the next occurrence of '\n' or '\r', or {@link #limit} if none is found.
*/
private int findNextLineTerminator(Charset charset) {
int stride;
if (charset.equals(Charsets.UTF_8) || charset.equals(Charsets.US_ASCII)) {
stride = 1;
} else if (charset.equals(Charsets.UTF_16)
|| charset.equals(Charsets.UTF_16LE)
|| charset.equals(Charsets.UTF_16BE)) {
stride = 2;
} else {
throw new IllegalArgumentException("Unsupported charset: " + charset);
}
for (int i = position; i < limit - (stride - 1); i += stride) {
if ((charset.equals(Charsets.UTF_8) || charset.equals(Charsets.US_ASCII))
&& Util.isLinebreak(data[i])) {
return i;
} else if ((charset.equals(Charsets.UTF_16) || charset.equals(Charsets.UTF_16BE))
&& data[i] == 0x00
&& Util.isLinebreak(data[i + 1])) {
return i;
} else if (charset.equals(Charsets.UTF_16LE)
&& data[i + 1] == 0x00
&& Util.isLinebreak(data[i])) {
return i;
}
}
return limit;
}
View on GitHub (pinned to dd430f7053)
Solutions
- Map unsupported charsets to a supported equivalent before reading: ISO-8859-1/windows-1252 -> US_ASCII or UTF-8 if bytes allow; UTF-32 -> decode the whole buffer manually with new String(bytes, charset)
- Read the line as bytes in a supported charset (UTF-8/US_ASCII) and re-decode the resulting String if you need a different character set afterwards
- Pre-validate: keep a Set of Charsets.UTF_8/US_ASCII/UTF_16/UTF_16LE/UTF_16BE and check charset membership before calling readSingleLine*
- For genuinely non-UTF encodings, bypass these line helpers and implement your own delimiter scan at byte level
Example fix
// before Charset cs = Charset.forName(headerCharset); // e.g. iso-8859-1 String line = p.readSingleLine(lineLength, cs); // throws // after Charset cs = normalize(headerCharset); // maps latin-1/cp1252 -> US_ASCII when safe, else UTF-8 String line = p.readSingleLine(lineLength, cs);
Defensive patterns
Strategy: type-guard
Validate before calling
private static final Set<Charset> LINE_CHARSETS = Set.of(
Charsets.UTF_8, Charsets.US_ASCII,
Charsets.UTF_16, Charsets.UTF_16LE, Charsets.UTF_16BE);
if (!LINE_CHARSETS.contains(charset)) charset = Charsets.UTF_8; Type guard
static boolean isLineReadableCharset(Charset cs) {
return cs.equals(Charsets.UTF_8) || cs.equals(Charsets.US_ASCII)
|| cs.equals(Charsets.UTF_16) || cs.equals(Charsets.UTF_16LE)
|| cs.equals(Charsets.UTF_16BE);
} Try / catch
Not applicable — validate the Charset before calling readSingleLine*; an IllegalArgumentException here is a programming error.
Prevention
- Whitelist charsets at the boundary where headers are parsed
- Map ISO-8859-1/windows-1252 labels to US_ASCII/UTF-8 handling for readSingleLine, then re-decode the String if needed
- Keep a lint/test rule that only the five supported Charsets reach ParsableByteArray line APIs
When it happens
Trigger: Calling ParsableByteArray.readSingleLine(int length, Charset) or readSingleLineWithDelimiter with Charsets.ISO_8859_1, Charset.forName("UTF-32"), or any non-standard Charset instance. The API accepts any Charset at the type level but only enumerates the five supported constants at runtime.
Common situations: Reading HTTP/ICY/HLS headers that declare charset=iso-8859-1 or windows-1252 in Content-Type and forwarding that Charset into the parser; feeding multi-byte UTF-32 streams; custom Datasources reusing ParsableByteArray for exotic encodings.
Related errors
- Unexpected color filter ${colorFilterSelection}
- The item must specify its mimeType
- ERROR_CODE_FAILED_RUNTIME_CHECK
- Cannot read frame at position ${lastDecodePosition}
- Multiple IMA server side ad insertion sources not supported.
AI-assisted analysis of google/ExoPlayer@dd430f7053 (2026-08-14).
Data as JSON: /api/errors/ca919bc4caf82251.
Report an issue: GitHub.