dromara/Sa-Token · error · IllegalArgumentException
非法Emoji:
Error message
非法Emoji:
What it means
Thrown by SaSerializerForBase64UseEmoji.stringToBytes when a 2-char unit of the encoded string is not a key in the EMOJI_MAP (IllegalArgumentException '非法Emoji'). The emoji serializer encodes each 6-bit index as one emoji composed of two UTF-16 chars; an unrecognized pair means the input was not produced by this serializer or was corrupted.
Source
Thrown at sa-token-plugin/sa-token-serializer-features/src/main/java/cn/dev33/satoken/serializer/SaSerializerForBase64UseEmoji.java:88
for (int index : indices) {
result.append(EMOJI_TABLE.get(index));
}
return result.toString();
}
@Override
public byte[] stringToBytes(String encoded) {
List<Integer> indices = new ArrayList<>();
// 提取索引(每个Emoji占2个char)
for (int i = 0; i < encoded.length(); ) {
if (i + 1 >= encoded.length()) break;
String emoji = encoded.substring(i, i + 2);
i += 2;
Integer index = EMOJI_MAP.get(emoji);
if (index == null) {
throw new IllegalArgumentException("非法Emoji: " + emoji);
}
indices.add(index);
}
// 转换为二进制字符串
StringBuilder binaryStr = new StringBuilder();
for (int index : indices) {
binaryStr.append(String.format("%6s", Integer.toBinaryString(index))
.replace(' ', '0'));
}
// 转换为字节数组(自动处理末尾补零)
List<Byte> bytes = new ArrayList<>();
for (int i = 0; i < binaryStr.length(); i += 8) {
int endIndex = Math.min(i + 8, binaryStr.length());
String byteStr = binaryStr.substring(i, endIndex);
if (byteStr.length() < 8) break; // 忽略末尾不足8位的部分
bytes.add((byte) Integer.parseInt(byteStr, 2));View on GitHub (pinned to ac2c7f6e94)
Solutions
- Ensure encode and decode both use SaSerializerForBase64UseEmoji on all services sharing the values
- Guarantee UTF-8 end to end (DB charset, Content-Type, connector parameters) so surrogate pairs survive
- Avoid any transformation (trim/replace/case change) of emoji-serialized strings in transit
Defensive patterns
Strategy: validation
Validate before calling
boolean ok = input.length() % 2 == 0;
for (int i = 0; ok && i < input.length(); i += 2) { ok = EMOJI_MAP.containsKey(input.substring(i, i + 2)); }
if (!ok) throw new IllegalArgumentException("string is not emoji-serializer output"); Try / catch
try { byte[] data = emojiSerializer.stringToBytes(input); } catch (IllegalArgumentException e) { log.warn("emoji decode failed — check UTF-8 integrity"); reSerialize(); } Prevention
- Force UTF-8 on every hop: connector, DB charset, Content-Type
- Never modify emoji-serialized strings in transit (no trim, no regex on them)
When it happens
Trigger: Feeding a plain, Base64, or mojibake string to the emoji decoder; UTF-8 encoding issues (e.g. a filter converting to ISO-8859-1) breaking surrogate pairs so substring(i, i+2) no longer aligns to emoji boundaries.
Common situations: Token serialized with a different serializer then read with the emoji one; database or HTTP layer mangling UTF-8; string manipulation (trim, case-folding) that shifts char alignment; serializer config mismatch between issuing and validating services.
Related errors
AI-assisted analysis of dromara/Sa-Token@ac2c7f6e94 (2026-08-14).
Data as JSON: /api/errors/2b951be73beb9b24.
Report an issue: GitHub.