chinabugotech/hutool · error · UtilException

BAD_INPUT

Error message

BAD_INPUT

What it means

Thrown by PunyCode.decode() when the input string ends prematurely mid-variable-length-integer, i.e., when the inner decoding loop reaches the end of the input (d == length) before completing the current digit sequence. This indicates a malformed or truncated Punycode string that cannot be fully parsed.

Source

Thrown at hutool-core/src/main/java/cn/hutool/core/codec/PunyCode.java:204

		int d = input.lastIndexOf(DELIMITER);
		if (d > 0) {
			for (int j = 0; j < d; j++) {
				final char c = input.charAt(j);
				if (isBasic(c)) {
					output.append(c);
				}
			}
			d++;
		} else {
			d = 0;
		}
		final int length = input.length();
		while (d < length) {
			int oldi = i;
			int w = 1;
			for (int k = BASE; ; k += BASE) {
				if (d == length) {
					throw new UtilException("BAD_INPUT");
				}
				int c = input.charAt(d++);
				int digit = codepoint2digit(c);
				if (digit > (Integer.MAX_VALUE - i) / w) {
					throw new UtilException("OVERFLOW");
				}
				i = i + digit * w;
				int t;
				if (k <= bias) {
					t = TMIN;
				} else if (k >= bias + TMAX) {
					t = TMAX;
				} else {
					t = k - bias;
				}
				if (digit < t) {
					break;
				}

View on GitHub (pinned to 8870454b2a)

Solutions

  1. Validate the Punycode string is well-formed before decoding — check it only contains [a-z0-9-] characters.
  2. Catch UtilException around decode() and report the input as malformed.
  3. Use decodeDomain() for full domain processing which handles the xn-- prefix correctly per label.

Example fix

// before
String decoded = PunyCode.decode("xn--fiq"); // truncated — throws BAD_INPUT

// after
try {
    String decoded = PunyCode.decode(input);
} catch (UtilException e) {
    if ("BAD_INPUT".equals(e.getMessage())) {
        return input; // fall back to raw string
    }
    throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate Punycode charset before decoding
boolean valid = input != null && input.matches("^[a-z0-9-]*$");

Try / catch

try {
    String decoded = PunyCode.decode(input);
} catch (UtilException e) {
    if ("BAD_INPUT".equals(e.getMessage())) {
        return input; // fall back to raw string if not valid Punycode
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling PunyCode.decode(input) with a string that is truncated or malformed — the decoder expects a complete sequence of base-36 digit characters but encounters the end of the string mid-sequence. This happens with corrupted, manually truncated, or incorrectly generated Punycode strings.

Common situations: Truncated domain names from user input or URL parsing. Corrupted data in transit or storage. Manually constructed or partially overwritten Punycode strings. Inconsistent prefix handling (passing 'xn--' prefix to decode() which strips it, leaving a malformed remainder).

Related errors


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