risingwavelabs/risingwave · error

unable to send sealed epoch

Error message

unable to send sealed epoch

What it means

While truncating an epoch, the in-memory log store tried to notify the sealed-epoch watcher via a broadcast channel but all receivers were dropped, so the send failed. This indicates the sealed-epoch consumer side is gone.

Source

Thrown at src/stream/src/common/log_store_impl/in_mem.rs:262

            return Err(anyhow!(
                "truncate at {:?} but latest offset is {:?}",
                offset,
                self.latest_offset
            ));
        }

        if let AwaitingTruncate {
            sealed_epoch,
            next_epoch,
        } = &self.epoch_progress
            && let TruncateOffset::Barrier { epoch } = offset
            && epoch == *sealed_epoch
        {
            let sealed_epoch = *sealed_epoch;
            self.epoch_progress = Consuming(*next_epoch);
            self.truncated_epoch_tx
                .send(sealed_epoch)
                .map_err(|_| anyhow!("unable to send sealed epoch"))?;
        }
        self.truncate_offset = offset;
        Ok(())
    }

    async fn rewind(&mut self) -> LogStoreResult<()> {
        Err(anyhow!("should not call rewind on it"))
    }

    async fn start_from(&mut self, _start_offset: Option<u64>) -> LogStoreResult<()> {
        Ok(())
    }
}

impl LogWriter for BoundedInMemLogStoreWriter {
    async fn init(
        &mut self,
        epoch: EpochPair,

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Check the receiver task for panics or early termination in logs
  2. Ensure the sealed-epoch consumer is spawned and alive before truncation happens
  3. Fix shutdown ordering so the log store is not truncating after consumers exit
Defensive patterns

Strategy: try-catch

Try / catch

match log_store.truncate(offset).await {
    Err(e) if e.to_string().contains("unable to send sealed epoch") => {
        tracing::warn!("sealed-epoch receiver gone; check consumer task");
        // restart consumer or surface shutdown
    }
    other => other?,
}

Prevention

When it happens

Trigger: truncate moves epoch progress past a sealed epoch and calls truncated_epoch_tx.send(sealed_epoch) when no receiver is alive.

Common situations: The sealed-epoch reader task panicked or was dropped before truncation, shutdown races between the log store and its consumers, or incorrect initialization dropping the receiver.

Related errors


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