risingwavelabs/risingwave · error · MetadataModelError

Failed to decode prost: field not found `{}`

Error message

Failed to decode prost: field not found `{}`

What it means

This converts a `PbFieldNotFound` into `MetadataModelError::InternalError` with the message 'Failed to decode prost: field not found'. It fires when generated protobuf accessor code is invoked for a field that the decoded message does not contain — usually a schema mismatch where the stored/parsed message predates a field that the code assumes exists.

Source

Thrown at src/meta/src/model/error.rs:37

pub type MetadataModelResult<T> = std::result::Result<T, MetadataModelError>;

#[derive(Error, Debug)]
pub enum MetadataModelError {
    #[error("Pb decode error: {0}")]
    PbDecode(#[from] prost::DecodeError),

    #[error(transparent)]
    InternalError(
        #[from]
        #[backtrace]
        anyhow::Error,
    ),
}

impl From<PbFieldNotFound> for MetadataModelError {
    fn from(p: PbFieldNotFound) -> Self {
        MetadataModelError::InternalError(anyhow::anyhow!(
            "Failed to decode prost: field not found `{}`",
            p.0
        ))
    }
}

impl From<MetadataModelError> for tonic::Status {
    fn from(e: MetadataModelError) -> Self {
        e.to_status(tonic::Code::Internal, "meta")
    }
}

impl MetadataModelError {
    pub fn internal(msg: impl ToString) -> Self {
        MetadataModelError::InternalError(anyhow!(msg.to_string()))
    }
}

View on GitHub (pinned to 6469eb736d)

Solutions

  1. Align versions across all RisingWave components so the metadata schema includes the field being read.
  2. Make the access defensive: use `field` (Option-returning) instead of `get_field` and handle the None case explicitly.
  3. Recreate the affected metadata object if it predates the schema addition and cannot be backfilled.
  4. Add a migration/backfill step when introducing new required metadata fields.

Example fix

// before
let id = object.get_id().unwrap(); // panics/errors if absent
// after
let id = object.id.ok_or_else(|| MetadataModelError::from(PbFieldNotFound("id")))?;
Defensive patterns

Strategy: type-guard

Validate before calling

// Use Option-returning accessor instead of get_xxx
let field = msg.field.as_ref()
    .ok_or_else(|| anyhow!("field not present; metadata written by older version"))?;

Type guard

fn has_field<'a, T>(opt: &'a Option<T>) -> Option<&'a T> {
    opt.as_ref()
}

Try / catch

match decode_and_use_model(bytes).await {
    Err(MetadataModelError::InternalError(e))
        if e.to_string().contains("field not found") => {
        tracing::error!("metadata predates schema; backfill or recreate object: {e:#}");
    }
    other => { /* normal handling */ }
}

Prevention

When it happens

Trigger: Calling a generated getter `get_<field>()` on a prost message where the optional/oneof field is unset, e.g. decoding an old-format metadata object then reading a newly added required field.

Common situations: Version skew: newer code reading metadata written by an older RisingWave that lacked the field; incomplete DDL/migration leaving partial metadata records; hand-edited or replayed protobuf payloads in tests.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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