prestodb/presto · error · IllegalArgumentException

Invalid decimal value '{stringValue}'

Error message

Invalid decimal value '{stringValue}'

What it means

Decimals.parse first matches the input string against DECIMAL_PATTERN; any string that is not a syntactically valid decimal literal (optional sign, digits, optional fraction) throws IllegalArgumentException 'Invalid decimal value ...'. This is a strict parser used when converting string form to a typed decimal, so malformed input is rejected before any numeric work.

Source

Thrown at presto-common/src/main/java/com/facebook/presto/common/type/Decimals.java:91

        return BIG_INTEGER_POWERS_OF_TEN[n];
    }

    public static DecimalParseResult parse(String stringValue)
    {
        return parse(stringValue, false);
    }

    // visible for testing
    public static DecimalParseResult parseIncludeLeadingZerosInPrecision(String stringValue)
    {
        return parse(stringValue, true);
    }

    private static DecimalParseResult parse(String stringValue, boolean includeLeadingZerosInPrecision)
    {
        Matcher matcher = DECIMAL_PATTERN.matcher(stringValue);
        if (!matcher.matches()) {
            throw new IllegalArgumentException("Invalid decimal value '" + stringValue + "'");
        }

        String sign = getMatcherGroup(matcher, 1);
        if (sign.isEmpty()) {
            sign = "+";
        }
        String leadingZeros = getMatcherGroup(matcher, 3);
        String integralPart = getMatcherGroup(matcher, 4);
        String fractionalPart = getMatcherGroup(matcher, 6);

        if (leadingZeros.isEmpty() && integralPart.isEmpty() && fractionalPart.isEmpty()) {
            throw new IllegalArgumentException("Invalid decimal value '" + stringValue + "'");
        }

        int scale = fractionalPart.length();
        int precision;
        if (includeLeadingZerosInPrecision) {
            precision = leadingZeros.length() + integralPart.length() + scale;

View on GitHub (pinned to 55bb57d202)

Solutions

  1. Pre-validate/normalize the string with a regex or BigDecimal.parse before calling Decimals.parse
  2. Scrub ingestion data (map non-numeric sentinels to NULL) before casting to DECIMAL
  3. Parse with new BigDecimal(value) first to normalize scientific notation, then re-serialize to a plain string
  4. Trim whitespace and strip locale grouping separators before parsing

Example fix

// before
long unscaled = Decimals.parse(userInput).getUnscaledValue(); // throws on '1,234.5'
// after
String normalized = userInput.trim().replace(",", "");
if (!normalized.matches("[+-]?\\d*(\\.\\d*)?")) {
    normalized = null; // treat as NULL
}
DecimalParseResult result = normalized == null ? null : Decimals.parse(normalized);
Defensive patterns

Strategy: validation

Validate before calling

private static final Pattern SAFE_DECIMAL = Pattern.compile("[+-]?\\d*(\\.\\d*)?");
public static boolean isParseableDecimal(String s) {
    return s != null && SAFE_DECIMAL.matcher(s.trim().replace(",", "")).matches()
        && s.matches(".*\\d.*");
}

Type guard

public static boolean isNumericLiteral(String s) {
    return s != null && !s.trim().isEmpty() && s.trim().matches("[+-]?\\d+(\\.\\d+)?|\\.\\d+|\\d+\\.");
}

Try / catch

try {
    DecimalParseResult r = Decimals.parse(value);
} catch (IllegalArgumentException e) {
    // treat as NULL / route to bad-record path
}

Prevention

When it happens

Trigger: Calling Decimals.parse / parseIncludeLeadingZerosInPrecision with a string that fails DECIMAL_PATTERN: empty string, 'abc', '1.2.3', '1e5', thousands separators like '1,000', trailing signs like '12-', or multiple decimal points.

Common situations: User-supplied string literals cast to DECIMAL; CSV/JSON ingestion where a non-numeric placeholder ('N/A', '--') reaches decimal parsing; locale-formatted numbers with commas or spaces.

Related errors


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/6e2c3027759d93e4. Report an issue: GitHub.