databendlabs/databend · error · InvalidData
Invalid version
Error message
Invalid version
What it means
PermitEntry (semaphore plugin storage) implements a versioned binary Decode trait. The first byte of the serialized record is a version tag; if it does not equal CURRENT_VERSION, decode refuses the record with io::ErrorKind::InvalidData 'Invalid version' rather than guessing at an incompatible layout.
Solutions
- Upgrade (or align) the Databend/plugin version with the one that wrote the semaphore permit entries.
- Clear or migrate the stale permit entries in the meta store so they are rewritten with CURRENT_VERSION.
- Verify the stored bytes are not corrupted (version byte flipped); restore meta from backup if corruption is confirmed.
- If intentional, bump the decode logic to accept/migrate the old version before returning the error.
Example fix
// before
let version = u8::decode(&mut r)?;
if version != CURRENT_VERSION {
return Err(Error::new(io::ErrorKind::InvalidData, "Invalid version"));
}
// after
let version = u8::decode(&mut r)?;
if version > CURRENT_VERSION {
return Err(Error::new(io::ErrorKind::InvalidData, "Invalid version"));
}
if version < CURRENT_VERSION {
// migrate from legacy layout
} Defensive patterns
Strategy: try-catch
Validate before calling
fn is_current_version_permit(buf: &[u8]) -> bool {
buf.first() == Some(&CURRENT_VERSION)
} Try / catch
match PermitEntry::decode(reader) {
Err(e) if e.kind() == io::ErrorKind::InvalidData && e.to_string() == "Invalid version" => {
// skip or migrate legacy record
migrate_or_drop_record(key)?;
Ok(None)
}
other => other,
} Prevention
- Align plugin/schema versions across the cluster before rolling upgrades.
- Store and check the record version before decoding legacy entries.
- Add migration logic for old versions instead of failing hard when formats evolve.
When it happens
Trigger: Calling PermitEntry::decode on bytes whose version byte differs from CURRENT_VERSION — e.g. records written by an older or newer plugin version, or arbitrary/garbage bytes being decoded as a permit entry.
Common situations: Upgrading or downgrading Databend while the semaphore plugin's on-disk meta records use a different schema version; corrupted meta storage where the version byte was overwritten; manually crafted test fixtures using the wrong version byte.
Understand the failure class
Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.
Related errors
- custom-encoded value too short to contain 4-byte header
- unknown flags in custom-encoded value header
- Access denied: is outside allowed directories
- {e}
- e
AI-assisted analysis of databendlabs/databend@288d84d76e (2026-09-11).
Data as JSON: /api/errors/6a16180c30b288e7.
Report an issue: GitHub.
Appendix: source
Thrown at src/meta/plugins/semaphore/src/storage/permit_entry.rs:51
pub id: String,
pub permits: u64,
}
impl Encode for PermitEntry {
fn encode<W: Write>(&self, mut w: W) -> Result<usize, io::Error> {
let mut n = 0;
n += CURRENT_VERSION.encode(&mut w)?;
n += self.id.encode(&mut w)?;
n += self.permits.encode(&mut w)?;
Ok(n)
}
}
impl Decode for PermitEntry {
fn decode<R: Read>(mut r: R) -> Result<Self, io::Error> {
let version = u8::decode(&mut r)?;
if version != CURRENT_VERSION {
return Err(Error::new(io::ErrorKind::InvalidData, "Invalid version"));
}
let id = Decode::decode(&mut r)?;
let permits = Decode::decode(&mut r)?;
Ok(PermitEntry { id, permits })
}
}
impl fmt::Display for PermitEntry {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "PermitEntry(id:{}, n:{})", self.id, self.permits)
}
}
impl PermitEntry {
pub fn new(id: impl ToString, value: u64) -> Self {
Self {
id: id.to_string(),View on GitHub (pinned to 288d84d76e)