{"record":{"id":"7134dad0ebad2de6","repo":"prestodb/presto","slug":"decimal-out-of-bound","errorCode":null,"errorMessage":"Decimal out of bound: ","messagePattern":"Decimal out of bound: ","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"presto-parquet/src/main/java/com/facebook/presto/parquet/spark/VariantUtil.java","lineNumber":317,"sourceCode":"\n    // Get a double value from variant value `value[position...]`.\n    // Throw `MALFORMED_VARIANT` if the variant is malformed.\n    public static double getDouble(byte[] value, int position)\n    {\n        checkIndex(position, value.length);\n        int basicType = value[position] & BASIC_TYPE_MASK;\n        int typeInfo = (value[position] >> BASIC_TYPE_BITS) & TYPE_INFO_MASK;\n        if (basicType != PRIMITIVE || typeInfo != DOUBLE) {\n            throw unexpectedType(Type.DOUBLE);\n        }\n        return Double.longBitsToDouble(readLong(value, position + 1, 8));\n    }\n\n    // Check whether the precision and scale of the decimal are within the limit.\n    private static void checkDecimal(BigDecimal decimal, int maxPrecision)\n    {\n        if (decimal.precision() > maxPrecision || decimal.scale() > maxPrecision) {\n            throw new IllegalArgumentException(\"Decimal out of bound: \" + decimal);\n        }\n    }\n\n    // Get a decimal value from variant value `value[position...]`.\n    // Throw `MALFORMED_VARIANT` if the variant is malformed.\n    public static BigDecimal getDecimal(byte[] value, int position)\n    {\n        checkIndex(position, value.length);\n        int basicType = value[position] & BASIC_TYPE_MASK;\n        int typeInfo = (value[position] >> BASIC_TYPE_BITS) & TYPE_INFO_MASK;\n        if (basicType != PRIMITIVE) {\n            throw unexpectedType(Type.DECIMAL);\n        }\n        // Interpret the scale byte as unsigned. If it is a negative byte, the unsigned value must be\n        // greater than `MAX_DECIMAL16_PRECISION` and will trigger an error in `checkDecimal`.\n        int scale = value[position + 1] & 0xFF;\n        BigDecimal result;\n        switch (typeInfo) {","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-parquet/src/main/java/com/facebook/presto/parquet/spark/VariantUtil.java#L299-L335","documentation":"checkDecimal is the precision/scale guard used by VariantUtil.getDecimal. The Variant encoding stores decimals as DECIMAL4/8/16 with a scale byte; the decoded BigDecimal must fit the maximum precision allowed for the backing integer width (4, 8, or 16 bytes). This IllegalArgumentException is thrown when the decoded decimal's precision exceeds maxPrecision or its scale exceeds maxPrecision, meaning the variant bytes encode a decimal this layout cannot legally represent.","triggerScenarios":"Calling VariantUtil.getDecimal(byte[] value, int position) on a variant whose DECIMAL4/DECIMAL8/DECIMAL16 payload decodes to a BigDecimal with precision > maxPrecision (32/64/128 as per the calling width) or scale > maxPrecision — i.e. corrupt or out-of-spec variant bytes.","commonSituations":"Variant data written by a non-conforming writer; corrupted decimal payload bytes; manually decoding raw variant bytes with the wrong assumed width so the decoded value overflows the allowed precision.","solutions":["Regenerate or re-read the source data to rule out corruption in the variant decimal payload","Check the writing engine's Variant decimal encoding matches the spec (scale byte + little-endian unscaled value within width)","Validate the decoded BigDecimal's precision/scale bounds yourself before use and reject malformed rows","Catch IllegalArgumentException around getDecimal and skip/log the offending row"],"exampleFix":"// before\nBigDecimal d = VariantUtil.getDecimal(value, pos);\n// after\nBigDecimal d;\ntry {\n    d = VariantUtil.getDecimal(value, pos);\n} catch (IllegalArgumentException e) {\n    throw new IllegalArgumentException(\"Malformed variant decimal in row\", e);\n}","handlingStrategy":"validation","validationCode":"// Java\nstatic boolean isDecimalInBound(BigDecimal d, int maxPrecision) {\n    return d.precision() <= maxPrecision && d.scale() <= maxPrecision;\n}\n// call before relying on the decoded value:\n// isDecimalInBound(decoded, 38) // 128-bit variant decimal limit","typeGuard":null,"tryCatchPattern":"// Java\ntry {\n    BigDecimal d = VariantUtil.getDecimal(value, pos);\n} catch (IllegalArgumentException e) {\n    // reject the row as malformed variant decimal\n}","preventionTips":["Only decode decimals with the width (4/8/16) matching the variant type code","Sanity-check decoded precision/scale against the spec limits before use","Verify the writing engine conforms to the Variant decimal encoding spec","Log and quarantine corrupt rows instead of failing the whole scan"],"tags":["variant","decimal","malformed-data","precision-overflow"],"backgroundTag":"decimal-precision-out-of-range","analyzedSha":"55bb57d202de3b926896fa966c2c4a44c779634e","analyzedAt":"2026-09-04T12:50:26.162Z","contentChangedAt":"2026-09-04T12:50:26.162Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}