risingwavelabs/risingwave · error · MemTableError

Inconsistent operation on table

Error message

Inconsistent operation on table {table_id} {key:?}, prev: {prev:?}, new: {new:?}

What it means

A MemTable received a KeyOp that is inconsistent with the operation already recorded for the same key (e.g. inserting a key that was previously deleted, or conflicting delete/insert sequences). This enforces per-key operation consistency in the shared buffer memtable.

Solutions

  1. Inspect the reported prev and new KeyOp values to understand the conflict
  2. Trace the writer producing the key ops (epoch, actor/task id) for a logic bug
  3. Verify the upstream source or MV definition is not re-emitting conflicting ops for the same key
  4. If triggered by malformed user data, validate/normalize input before writing
Defensive patterns

Strategy: try-catch

Try / catch

match memtable.write(...).await {
    Err(e) if matches!(e.as_ref(), MemTableError::InconsistentOperation { .. }) => {
        tracing::error!(error = ?e, "conflicting key op");
        // surface as data-corruption / trigger recovery
    }
    other => other?,
}

Prevention

When it happens

Trigger: Writing to a table where the same key already has a conflicting KeyOp (prev vs new), detected in MemTable::write under OpConsistencyLevel checks.

Common situations: Bugs in stream/batch write paths that replay or interleave operations incorrectly, upstream executor bugs re-writing tombstoned keys, or corrupted input data causing out-of-order operations.

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/5a4180b8b473f6d5. Report an issue: GitHub.

Appendix: source

Thrown at src/storage/src/mem_table.rs:56

pub enum KeyOp {
    Insert(Bytes),
    Delete(Bytes),
    /// (`old_value`, `new_value`)
    Update((Bytes, Bytes)),
}

/// `MemTable` is a buffer for modify operations without encoding
#[derive(Clone)]
pub struct MemTable {
    table_id: TableId,
    pub(crate) buffer: MemTableStore,
    pub(crate) op_consistency_level: OpConsistencyLevel,
    pub(crate) kv_size: KvSize,
}

#[derive(Error, Debug)]
pub enum MemTableError {
    #[error("Inconsistent operation on table {table_id} {key:?}, prev: {prev:?}, new: {new:?}")]
    InconsistentOperation {
        table_id: TableId,
        key: TableKey<Bytes>,
        prev: KeyOp,
        new: KeyOp,
    },
}

type Result<T> = std::result::Result<T, Box<MemTableError>>;

pub type MemTableStore = BTreeMap<TableKey<Bytes>, KeyOp>;
pub struct MemTableIteratorBuilder;
pub struct MemTableRevIteratorBuilder;

fn map_to_hummock_value<'a>(
    (key, op): (&'a TableKey<Bytes>, &'a KeyOp),
) -> (TableKey<&'a [u8]>, HummockValue<&'a [u8]>) {
    (

View on GitHub (pinned to 6469eb736d)