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
- Align versions across all RisingWave components so the metadata schema includes the field being read.
- Make the access defensive: use `field` (Option-returning) instead of `get_field` and handle the None case explicitly.
- Recreate the affected metadata object if it predates the schema addition and cannot be backfilled.
- 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
- Prefer Option-returning prost accessors over get_xxx for newly added fields
- Add migrations/backfills when introducing required metadata fields
- Avoid reading metadata written by older versions without a compatibility path
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Pb decode error: {0}
- None msg in request
- empty commit metadata
- empty vnode bitmap
- query_epoch not set in distributed lookup join
AI-assisted analysis of risingwavelabs/risingwave@6469eb736d (2026-09-11).
Data as JSON: /api/errors/5116435247a409c5.
Report an issue: GitHub.