chinabugotech/hutool · error · IllegalArgumentException

Invalid char '{}' at [{}]

Error message

Invalid char '{}' at [{}]

What it means

Base58Codec's decoder maps each input char through a lookup table; chars not in the Base58 alphabet (excludes 0, O, I, l and all non-alphanumeric) or any char >= 128 yield digit -1 and an IllegalArgumentException naming the char and index. This is the decode-side input validation, distinct from the checksum check (error 37).

Source

Thrown at hutool-core/src/main/java/cn/hutool/core/codec/Base58Codec.java:137

			final int length = alphabet.length();
			for (int i = 0; i < length; i++) {
				lookupTable[alphabet.charAt(i)] = (byte) i;
			}
			this.lookupTable = lookupTable;
		}

		@Override
		public byte[] decode(CharSequence encoded) {
			if (encoded.length() == 0) {
				return new byte[0];
			}
			// Convert the base58-encoded ASCII chars to a base58 byte sequence (base58 digits).
			final byte[] input58 = new byte[encoded.length()];
			for (int i = 0; i < encoded.length(); ++i) {
				char c = encoded.charAt(i);
				int digit = c < 128 ? lookupTable[c] : -1;
				if (digit < 0) {
					throw new IllegalArgumentException(StrUtil.format("Invalid char '{}' at [{}]", c, i));
				}
				input58[i] = (byte) digit;
			}
			// Count leading zeros.
			int zeros = 0;
			while (zeros < input58.length && input58[zeros] == 0) {
				++zeros;
			}
			// Convert base-58 digits to base-256 digits.
			byte[] decoded = new byte[encoded.length()];
			int outputStart = decoded.length;
			for (int inputStart = zeros; inputStart < input58.length; ) {
				decoded[--outputStart] = divmod(input58, inputStart, 58, 256);
				if (input58[inputStart] == 0) {
					++inputStart; // optimization - skip leading zeros
				}
			}
			// Ignore extra leading zeroes that were added during the calculation.

View on GitHub (pinned to 8870454b2a)

Solutions

  1. Trim and strip all whitespace before decoding.
  2. Validate the input matches the Base58 alphabet regex ^[1-9A-HJ-NP-Za-km-z]+$ before calling decode.
  3. Reject inputs containing the forbidden look-alike chars (0,O,I,l).

Example fix

// before
byte[] d = Base58.decode(" 1BvBMSE  YstWetqTFn5\n");
// after
String clean = userInput.trim().replaceAll("\\s", "");
if (!clean.matches("^[1-9A-HJ-NP-Za-km-z]+$")) throw new IllegalArgumentException("bad base58");
byte[] d = Base58.decode(clean);
Defensive patterns

Strategy: validation

Validate before calling

String clean = input.trim().replaceAll("\\s", "");
if (!clean.matches("^[1-9A-HJ-NP-Za-km-z]+$")) throw new IllegalArgumentException("invalid base58 char in: " + input);

Type guard

static boolean isBase58(String s) { return s != null && s.matches("^[1-9A-HJ-NP-Za-km-z]+$"); }

Try / catch

try { return Base58.decode(s); } catch (IllegalArgumentException e) { throw new IllegalArgumentException("bad base58", e); }

Prevention

When it happens

Trigger: Decoding a string with whitespace, 0/O/I/l, punctuation, or non-ASCII chars via Base58Codec.decode / Base58.decode. A copy-paste that included spaces or newlines.

Common situations: User-supplied Base58 with formatting whitespace; mistaking Base64/Base32 output for Base58; locale-specific characters in the input.

Related errors


AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14). Data as JSON: /api/errors/bc26bd07a3b5f6ee. Report an issue: GitHub.