{"record":{"id":"13c319222defd1f7","repo":"prestodb/presto","slug":"unexpected-multiple-column-statistics-for-node-s","errorCode":null,"errorMessage":"Unexpected multiple column statistics for node %s in row group %s in stripe at offset %s","messagePattern":"Unexpected multiple column statistics for node (.+?) in row group (.+?) in stripe at offset (.+?)","errorType":"exception","errorClass":"OrcCorruptionException","httpStatus":null,"severity":"error","filePath":"presto-orc/src/main/java/com/facebook/presto/orc/OrcWriteValidation.java","lineNumber":389,"sourceCode":"    private Map<Integer, ColumnStatistics> aggregateRowGroupStatisticsFromRowIndex(\n            OrcDataSourceId orcDataSourceId,\n            Map<StreamId, List<RowGroupIndex>> actualRowGroupStatistics,\n            long stripeOffset,\n            int rowGroupIndex)\n    {\n        // flattened nodes might have multiple ROW_INDEX with the same column, but different sequences\n        // aggregate such statistics before the validation\n        Map<Integer, List<ColumnStatistics>> actualColumnStatisticsByColumn = new HashMap<>();\n        for (Entry<StreamId, List<RowGroupIndex>> entry : actualRowGroupStatistics.entrySet()) {\n            int column = entry.getKey().getColumn();\n            ColumnStatistics actual = entry.getValue().get(rowGroupIndex).getColumnStatistics();\n            List<ColumnStatistics> aggregateStats = actualColumnStatisticsByColumn.computeIfAbsent(column, (key) -> new ArrayList<>());\n            aggregateStats.add(actual);\n\n            // Regular nodes have only 1 ColumnStatistics in the ROW_INDEX, flattened nodes\n            // might have zero or more column statistics.\n            if (aggregateStats.size() != 1 && !flattenedValueNodes.contains(column)) {\n                throw new OrcCorruptionException(\n                        orcDataSourceId,\n                        \"Unexpected multiple column statistics for node %s in row group %s in stripe at offset %s\",\n                        column,\n                        rowGroupIndex,\n                        stripeOffset);\n            }\n        }\n\n        return actualColumnStatisticsByColumn.entrySet().stream()\n                .collect(Collectors.toMap(Entry::getKey, entry -> mergeColumnStatistics(entry.getValue())));\n    }\n\n    private Map<Integer, Integer> getFlattenedKeyToMapNodes(Set<Integer> flattenedNodes, List<OrcType> orcTypes)\n    {\n        ImmutableMap.Builder<Integer, Integer> keyNodeToMapNode = ImmutableMap.builder();\n        flattenedNodes.forEach(mapNodeIndex -> keyNodeToMapNode.put(orcTypes.get(mapNodeIndex).getFieldTypeIndex(0), mapNodeIndex));\n        return keyNodeToMapNode.build();\n    }","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/prestodb/presto/blob/55bb57d202de3b926896fa966c2c4a44c779634e/presto-orc/src/main/java/com/facebook/presto/orc/OrcWriteValidation.java#L371-L407","documentation":"During write validation, Presto ORC aggregates the per-row-group ColumnStatistics read from the file's ROW_INDEX stream and compares them to statistics recorded at write time. A regular (non-flattened) column must produce exactly one ColumnStatistics per row group; this error means multiple statistics entries were aggregated for a node that is not a flattened value node, so the ROW_INDEX data is inconsistent with the writer's declared schema. The library throws OrcCorruptionException because the file likely was written by a different/buggy writer or is corrupted.","triggerScenarios":"Reading an ORC file where validateRowGroupStatistics (via actualStatistics/aggregateRowGroupStatisticsFromRowIndex) aggregates more than one ROW_INDEX ColumnStatistics for a column whose node is not in flattenedValueNodes — e.g. files written by older ORC writers, nested/complex-type handling differences, or corrupted stripe metadata.","commonSituations":"Files produced by other ORC writers (Hive, Spark) with different flattened-node metadata; ORC files corrupted in transit; mismatched presto-orc version reading files written by a newer/older writer.","solutions":["Verify the ORC file writer version and re-write the file with the same or newer writer version used by Presto","Confirm the file is not truncated/corrupted (checksum the object in storage, re-download)","Upgrade presto-orc/Presto to a version whose flattened-node handling matches the file's writer","If the file is known-good, disable write validation (orc.write-validation=false) to skip verification"],"exampleFix":"// before\n// reading a file written by an older writer fails validation\nSession session = ...; // orc_write_validation=true\n// after\nsession.setProperty(\"orc_write_validation\", false); // skip read-time write validation for legacy files","handlingStrategy":"validation","validationCode":"// Before reading, check writer version and skip validation for foreign writers\nif (!writerVersion.equals(file.getPostscript().getWriterVersion()) || file.getMetadataValue(\"write.validation\") == null) {\n    session.setProperty(\"orc_write_validation\", false);\n}","typeGuard":null,"tryCatchPattern":"try {\n    orcReader = new OrcReader(...);\n} catch (OrcCorruptionException e) {\n    if (e.getMessage().contains(\"Unexpected multiple column statistics\")) {\n        // fall back to reading with write validation disabled\n    }\n}","preventionTips":["Write ORC files with Presto's own writer when read-side validation is enabled","Keep writer and reader Presto versions aligned","Enable storage checksums to catch corruption before validation does","Don't post-process ORC files with tools that alter ROW_INDEX metadata"],"tags":["orc","corruption","statistics","write-validation"],"backgroundTag":"orc-write-validation-mismatch","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"}