apache/flink · error · NumberFormatException

Invalid character.

Error message

Invalid character.

What it means

Thrown by ByteParser.parseField during the digit loop when a byte inside the numeric field is outside '0'..'9'. The parser is strict: no thousands separators, no '+', no decimal point, no whitespace — only ASCII digits (checked via byte range 48-57) after an optional leading '-' are accepted for byte fields.

Source

Thrown at flink-core/src/main/java/org/apache/flink/types/parser/ByteParser.java:138

        if (bytes[startPos] == delimiter) {
            throw new NumberFormatException("Empty field.");
        }

        if (bytes[startPos] == '-') {
            neg = true;
            startPos++;
            length--;
            if (length == 0 || bytes[startPos] == delimiter) {
                throw new NumberFormatException("Orphaned minus sign.");
            }
        }

        for (; length > 0; startPos++, length--) {
            if (bytes[startPos] == delimiter) {
                return (byte) (neg ? -val : val);
            }
            if (bytes[startPos] < 48 || bytes[startPos] > 57) {
                throw new NumberFormatException("Invalid character.");
            }
            val *= 10;
            val += bytes[startPos] - 48;

            if (val > Byte.MAX_VALUE && (!neg || val > -Byte.MIN_VALUE)) {
                throw new NumberFormatException("Value overflow/underflow");
            }
        }
        return (byte) (neg ? -val : val);
    }
}

View on GitHub (pinned to 2f3c205e92)

Solutions

  1. Emit plain unformatted integers (no separators, no plus sign) for byte columns at the producer.
  2. If the value may be fractional or formatted, parse it as double/BigDecimal first and range-check before narrowing to byte.
  3. Pre-scan the field for non-digit bytes and route bad records to a dead-letter/side output.

Example fix

// before
byte v = ByteParser.parseField(bytes, start, len, '|'); // fails on "1,000"

// after
String field = new String(bytes, start, len, StandardCharsets.UTF_8).replace(",", "");
byte v = Byte.valueOf(field.trim());
Defensive patterns

Strategy: validation

Validate before calling

for (int i = 0; i < len; i++) {
    byte b = bytes[start + i];
    if (b == (byte) delimiter) break;
    if (b < 48 || b > 57) throw new IllegalArgumentException("Non-digit byte in byte field: " + (char) b);
}

Try / catch

try {
    byte v = ByteParser.parseField(bytes, start, len, delim);
} catch (NumberFormatException e) {
    String raw = new String(bytes, start, len, StandardCharsets.UTF_8);
    byte v = Byte.parseByte(raw.replace(",", "").trim()); // fallback for formatted input
}

Prevention

When it happens

Trigger: Calling ByteParser.parseField with fields like "1,000", "12 ", "0x1F", "+5", or "3.0" — any non-digit byte before the delimiter triggers the rejection at ByteParser.java:140.

Common situations: Locale-formatted numbers with grouping separators from spreadsheets; float values fed into a byte/int column; hex or signed-plus notation; schema drift where a byte column starts receiving formatted strings.

Understand the failure class

Related errors


AI-assisted analysis of apache/flink@2f3c205e92 (2026-08-14). Data as JSON: /api/errors/8a686b647bd8e506. Report an issue: GitHub.