databendlabs/databend · error

Operation::AsIs is not supported

Error message

Operation::AsIs is not supported

What it means

encode_operation converts a typed Operation<T> into its protobuf-encoded Operation<Vec<u8>>, and it only supports Update and Delete. Operation::AsIs (a directive meaning 'leave the existing value untouched') cannot be encoded into a value-carrying upsert, so reaching the match wildcard panics. The API contract is that callers must only pass Update or Delete to this encoder.

Solutions

  1. Do not pass Operation::AsIs to encode_operation; handle AsIs at the caller level (skip the write or resolve it to an explicit value first)
  2. Match on the operation before encoding: return Operation::AsIs passthrough or an error for unsupported variants
  3. If you need AsIs semantics, use a transaction API that natively supports the AsIs operation instead of the pb encoder
  4. Audit call sites of encode_operation and add debug asserts/tests that reject AsIs early

Example fix

// before
let encoded = sm.encode_operation(&op); // panics if op == AsIs
// after
let encoded = match op {
    Operation::Update(_) | Operation::Delete => sm.encode_operation(&op),
    Operation::AsIs => return Err(ErrorCode::InternalError("AsIs not encodable")),
};
Defensive patterns

Strategy: type-guard

Validate before calling

if matches!(op, Operation::AsIs) { return Err(ErrorCode::InternalError("AsIs not supported by encode_operation")); }

Type guard

fn is_encodable<T>(op: &Operation<T>) -> bool { !matches!(op, Operation::AsIs) }

Try / catch

// not a catchable error: it panics. Guard instead:
assert!(!matches!(op, Operation::AsIs), "AsIs must be resolved before encoding");

Prevention

When it happens

Trigger: Calling encode_operation (directly or via an upsert/SafeUpload path) with an Operation that is Operation::AsIs — e.g. generic meta write code that copies a caller-supplied Operation without normalizing AsIs first.

Common situations: New meta API code reusing encode_operation with share/AsIs-style semantics; refactors that forward Operations from higher layers without filtering; misuse of the pb_api by extension code.

Related errors


AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11). Data as JSON: /api/errors/44a7cdc18c2f4161. Report an issue: GitHub.

Appendix: source

Thrown at src/meta/api/src/kv/pb_api/compress.rs:68

impl Encoder {
    pub const fn new(compress: bool) -> Self {
        Self {
            compress: AtomicBool::new(compress),
        }
    }

    pub fn set_compress(&self, compress: bool) {
        self.compress.store(compress, Ordering::Relaxed);
    }

    /// Encode an upsert Operation of T into protobuf encoded value.
    pub fn encode_operation<T: FromToProto>(&self, value: &Operation<T>) -> Operation<Vec<u8>> {
        match value {
            Operation::Update(t) => Operation::Update(self.encode_pb(t)),
            Operation::Delete => Operation::Delete,
            _ => {
                unreachable!("Operation::AsIs is not supported")
            }
        }
    }

    /// Encode a `FromToProto` value to protobuf bytes, with optional zstd compression.
    pub fn encode_pb<T: FromToProto>(&self, value: &T) -> Vec<u8> {
        let p = value.to_pb();
        self.encode_value(prost::Message::encode_to_vec(&p))
    }

    /// Optionally compress `buf` with zstd.
    ///
    /// Returns `buf` unchanged if compression is disabled or `buf.len() < COMPRESS_THRESHOLD`.
    /// Otherwise prepends `[0x0F, FLAG_ZSTD, 0x00, 0x00]` and returns the compressed payload.
    /// Falls back to returning `buf` uncompressed on compression error.
    pub fn encode_value(&self, buf: Vec<u8>) -> Vec<u8> {
        if !self.compress.load(Ordering::Relaxed) {
            return buf;

View on GitHub (pinned to 288d84d76e)