chinabugotech/hutool · error · ValidateException

Base58 checksum is invalid

Error message

Base58 checksum is invalid

What it means

Base58 decoding (with checksum) splits the decoded bytes into payload + 4-byte trailing checksum, recomputes the checksum of the payload, and compares. A mismatch means the input was corrupted, truncated, or is not a valid Base58Check value, and ValidateException is thrown. This is the standard Bitcoin-style Base58Check integrity guard.

Source

Thrown at hutool-core/src/main/java/cn/hutool/core/codec/Base58.java:100

	 * @return 解码后的bytes
	 */
	public static byte[] decode(CharSequence encoded) {
		return Base58Codec.INSTANCE.decode(encoded);
	}

	/**
	 * 验证并去除验证位和版本位
	 *
	 * @param data        编码的数据
	 * @param withVersion 是否包含版本位
	 * @return 载荷数据
	 */
	private static byte[] verifyAndRemoveChecksum(byte[] data, boolean withVersion) {
		final byte[] payload = Arrays.copyOfRange(data, withVersion ? 1 : 0, data.length - CHECKSUM_SIZE);
		final byte[] checksum = Arrays.copyOfRange(data, data.length - CHECKSUM_SIZE, data.length);
		final byte[] expectedChecksum = checksum(payload);
		if (false == Arrays.equals(checksum, expectedChecksum)) {
			throw new ValidateException("Base58 checksum is invalid");
		}
		return payload;
	}

	/**
	 * 数据 + 校验码
	 *
	 * @param version 版本,{@code null}表示不添加版本位
	 * @param payload Base58数据(不含校验码)
	 * @return Base58数据
	 */
	private static byte[] addChecksum(Integer version, byte[] payload) {
		final byte[] addressBytes;
		if (null != version) {
			addressBytes = new byte[1 + payload.length + CHECKSUM_SIZE];
			addressBytes[0] = (byte) version.intValue();
			System.arraycopy(payload, 0, addressBytes, 1, payload.length);
		} else {

View on GitHub (pinned to 8870454b2a)

Solutions

  1. Confirm the input was produced by the checked encoder (Base58.encodeChecked); if it is raw Base58 use the non-checked decode path.
  2. Re-verify the source string character-for-character (single typo invalidates the checksum by design).
  3. Catch ValidateException and surface a 'corrupt input' error to the user rather than retrying.

Example fix

// before
byte[] payload = Base58.decodeChecked(userInput);
// after
try { byte[] payload = Base58.decodeChecked(userInput); }
catch (ValidateException e) { throw new IllegalArgumentException("address corrupt or mistyped", e); }
Defensive patterns

Strategy: try-catch

Validate before calling

// cannot validate checksum without decoding; ensure input came from encodeChecked
if (input == null || input.isEmpty()) throw new IllegalArgumentException("empty base58");

Try / catch

try { return Base58.decodeChecked(s); } catch (ValidateException e) { throw new IllegalArgumentException("corrupt Base58Check input", e); }

Prevention

When it happens

Trigger: Base58.decode / decodeChecked on a string whose last 4 bytes do not equal the SHA256-double hash of the payload; a typo in a Base58 address; using a raw (unchecked) Base58 string with a checked-decode API.

Common situations: Handling cryptocurrency addresses/keys that the user mistyped; reading Base58 from a transport that dropped/corrupted characters; mixing checked vs unchecked encode/decode.

Related errors


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