Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

parquet: panic (index out of bounds) reading FIXED_LEN_BYTE_ARRAY dictionary page with out-of-range index

Open Beginner friendly
#11,689 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@efegokdemir is already working on this.

Since Oct 19, 2026.

  • #11716 by @efegokdemir — open

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
83/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
rust
Domain
backend, databases

Research direction

Start in parquet/src/arrow/array_reader/fixed_len_byte_array.rs, at ValueDecoder::read, where the issue shows the dictionary buffer is sliced using the decoded key. Read the related out-of-range handling in RleDecoder::get_batch_with_dict from #9434, then run or adapt the reproduction in the issue. Done when an out-of-range dictionary index returns a ParquetError instead of panicking.

Written by the indexing model from the issue text.

Description

Describe the bug

Reading a FIXED_LEN_BYTE_ARRAY column whose data page contains a dictionary index that is out of range for the dictionary page panics instead of returning an error.

ValueDecoder::read in parquet/src/arrow/array_reader/fixed_len_byte_array.rs slices the dictionary buffer with key * byte_length without checking the key against the number of dictionary entries:

let offset = *key as usize * self.byte_length;
let val = &dict.as_ref()[offset..offset + self.byte_length];

RleDecoder::get_batch_with_dict returns an error for out-of-range indices (#9434), but this path indexes the dictionary directly.

To Reproduce

With parquet = "60.0.0", arrow-array = "60.0.0", arrow-schema = "60.0.0", bytes = "1":

use std::sync::Arc;

use arrow_array::{FixedSizeBinaryArray, RecordBatch};
use arrow_schema::{DataType, Field, Schema};
use bytes::Bytes;
use parquet::arrow::arrow_reader::ParquetRecordBatchReaderBuilder;
use parquet::arrow::ArrowWriter;
use parquet::file::properties::{WriterProperties, WriterVersion};

fn main() {
    // A FIXED_LEN_BYTE_ARRAY(4) column: 50 x "aaaa" then 50 x "bbbb", so the
    // data page holds dictionary indices as two RLE runs (0 then 1).
    let values: Vec<&[u8]> = (0..100).map(|i| if i < 50 { b"aaaa" } else { b"bbbb" } as &[u8]).collect();
    let array = FixedSizeBinaryArray::try_from_iter(values.into_iter()).unwrap();
    let schema = Arc::new(Schema::new(vec![Field::new("c", DataType::FixedSizeBinary(4), false)]));
    let batch = RecordBatch::try_new(schema.clone(), vec![Arc::new(array)]).unwrap();
    let mut buf = Vec::new();
    let props = WriterProperties::builder()
        .set_writer_version(WriterVersion::PARQUET_2_0) // dictionary for FLBA
        .set_dictionary_enabled(true)
        .build();
    let mut writer = ArrowWriter::try_new(&mut buf, schema, Some(props)).unwrap();
    writer.write(&batch).unwrap();
    writer.close().unwrap();

    // Data page values: bit width 1, RLE run (50 x 0), RLE run (50 x 1).
    // Change the bit width to 8 and the second run's index to 255; the
    // dictionary only has 2 entries.
    let pattern = [0x01, 0x64, 0x00, 0x64, 0x01];
    let pos = buf.windows(pattern.len()).position(|w| w == pattern).expect("pattern");
    buf[pos] = 8;
    buf[pos + 4] = 0xFF;

    let reader = ParquetRecordBatchReaderBuilder::try_new(Bytes::from(buf))
        .unwrap()
        .build()
        .unwrap();
    for batch in reader {
        println!("{:?}", batch.map(|b| b.num_rows()));
    }
}

Output (debug and release):

thread 'main' panicked at parquet-60.0.0/src/arrow/array_reader/fixed_len_byte_array.rs:497:49:
range start index 1020 out of range for slice of length 8

Backtrace (abridged):

<parquet::arrow::array_reader::fixed_len_byte_array::ValueDecoder as ColumnValueDecoder>::read::{{closure}}
parquet::arrow::decoder::dictionary_index::DictIndexDecoder::read
<parquet::arrow::array_reader::fixed_len_byte_array::ValueDecoder as ColumnValueDecoder>::read
parquet::column::reader::GenericColumnReader<R,D,V>::read_records_with_reservation
...
Expected behavior

The reader returns a ParquetError (e.g. "dictionary index out of bounds") instead of panicking.

Additional context
  • Reproduces with parquet 60.0.0 and on main at 33d338ddf (panic at fixed_len_byte_array.rs:432).
  • Found while fuzzing a Parquet reader built on arrow-rs. Related: #9434, #9742.
Dominant language
Rust
Stars
3.6k
Forks
1.4k
Avg merge
2d 23h
Merged PRs (30d)
253

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from apache/arrow-rs

All issues in apache/arrow-rs

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.