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
- Fix the caller so it only issues truncate calls with strictly increasing offsets (skip if offset <= previous truncate offset).
- Check how truncate_offset is persisted/recovered on actor restart; ensure recovery does not reset progress to an older barrier.
- Verify barrier alignment logic upstream is not re-injecting an already-processed barrier epoch.
- 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
- Track the last truncate offset in the caller and skip no-op/regressing truncations.
- Persist truncate progress across restarts so recovery does not rewind.
- Add an assertion/log when a barrier epoch regresses upstream.
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
- Exchange executor should not have children!
- truncate offset {:?} but prev truncate offset is {:?}
- truncate at {:?} but latest offset is {:?}
- unable to send init epoch
- unable to send stream chunk
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/6c5ab5ddbce5f4cb.
Report an issue: GitHub.