risingwavelabs/risingwave · error

Filter can only receive bool array

Error message

Filter can only receive bool array

What it means

The KV log store reader's `truncate` method was asked to truncate at an offset that is not newer than the offset of the most recent truncation already applied. Truncations must be monotonically increasing; re-truncating at or before the previous point would delete nothing new and indicates a bug in the caller's bookkeeping of progress (epoch/sequence offsets).

Source

Thrown at src/batch/executors/src/executor/filter.rs:68

    #[try_stream(boxed, ok = DataChunk, error = BatchError)]
    async fn do_execute(self: Box<Self>) {
        let mut data_chunk_builder =
            DataChunkBuilder::new(self.child.schema().data_types(), self.chunk_size);

        #[for_await]
        for data_chunk in self.child.execute() {
            let data_chunk = data_chunk?.compact_vis();
            let vis_array = self.expr.eval(&data_chunk).await?;

            if let Bool(vis) = vis_array.as_ref() {
                // TODO: should we yield masked data chunk directly?
                for data_chunk in
                    data_chunk_builder.append_chunk(data_chunk.with_visibility(vis.to_bitmap()))
                {
                    yield data_chunk;
                }
            } else {
                bail!("Filter can only receive bool array");
            }
        }

        if let Some(chunk) = data_chunk_builder.consume_all() {
            yield chunk;
        }
    }
}

impl BoxedExecutorBuilder for FilterExecutor {
    async fn new_boxed_executor(
        source: &ExecutorBuilder<'_>,
        inputs: Vec<BoxedExecutor>,
    ) -> Result<BoxedExecutor> {
        let [input]: [_; 1] = inputs.try_into().unwrap();

        let filter_node = try_match_expand!(
            source.plan_node().get_node_body().unwrap(),

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Fix the caller so it only issues truncate calls with strictly increasing offsets (skip if offset <= previous truncate offset).
  2. Check how truncate_offset is persisted/recovered on actor restart; ensure recovery does not reset progress to an older barrier.
  3. Verify barrier alignment logic upstream is not re-injecting an already-processed barrier epoch.
  4. If seen after a version change, confirm the Hummock version/epoch watermark fed to the reader advances monotonically.

Example fix

// before
reader.truncate(offset); // called unconditionally on every barrier
// after
if reader.last_truncate_offset().map_or(true, |prev| offset > prev) {
    reader.truncate(offset);
}
Defensive patterns

Strategy: validation

Validate before calling

// rust
fn safe_truncate(reader: &mut KvLogStoreReader, offset: TruncateOffset) -> anyhow::Result<()> {
    if let Some(prev) = reader.last_truncate_offset() {
        anyhow::ensure!(offset > prev, "skip stale truncate offset {:?} (prev {:?})", offset, prev);
    }
    reader.truncate(offset)
}

Prevention

When it happens

Trigger: Calling `KvLogStoreReader::truncate(offset)` where `offset <= self.truncate_offset`, while `offset.epoch() >= first_write_epoch` (i.e. truncating within the current buffered epoch range). Thrown at reader.rs:529.

Common situations: Stream executor recovery re-delivers an older barrier after a restart; Hummock version snapshots rewinding epoch progress; duplicated barrier handling in the upstream executor passing the same truncate offset twice; epoch/seq-id state not persisted across actor restarts.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11). Data as JSON: /api/errors/6c5ab5ddbce5f4cb. Report an issue: GitHub.