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.

Solutions

  1. Ensure encode and decode both use SaSerializerForBase64UseEmoji on all services sharing the values
  2. Guarantee UTF-8 end to end (DB charset, Content-Type, connector parameters) so surrogate pairs survive
  3. 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

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.

Appendix: 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)