linera-io/linera-protocol · error
Failed to deserialize event
Error message
Failed to deserialize event
What it means
ContractRuntime::read_event fetches raw event bytes from a subscribed stream via the host and then does bcs::from_bytes::<Application::EventValue>. The expect panics when the bytes stored in the stream cannot be decoded into the reader application's declared EventValue type — i.e. the writer's event schema and the reader's diverged.
Source
Thrown at linera-sdk/src/contract/runtime.rs:328
/// Adds a new item to an event stream. Returns the new event's index in the stream.
pub fn emit(&mut self, name: StreamName, value: &Application::EventValue) -> u32 {
contract_wit::emit(
&name.into(),
&bcs::to_bytes(value).expect("Failed to serialize event"),
)
}
/// Reads an event from a stream. Returns the event's value.
///
/// Fails the block if the event doesn't exist.
pub fn read_event(
&mut self,
chain_id: ChainId,
name: StreamName,
index: u32,
) -> Application::EventValue {
let event = contract_wit::read_event(chain_id.into(), &name.into(), index);
bcs::from_bytes(&event).expect("Failed to deserialize event")
}
/// Subscribes this application to an event stream.
pub fn subscribe_to_events(
&mut self,
chain_id: ChainId,
application_id: ApplicationId,
name: StreamName,
) {
contract_wit::subscribe_to_events(chain_id.into(), application_id.into(), &name.into())
}
/// Unsubscribes this application from an event stream.
pub fn unsubscribe_from_events(
&mut self,
chain_id: ChainId,
application_id: ApplicationId,
name: StreamName,View on GitHub (pinned to 6c226ddcb3)
Solutions
- Ensure the subscriber is built against the same crate version as the application that emits the stream, and redeploy the subscriber after the emitter's EventValue changes
- Use unique StreamNames per application (namespace them with the application name) so foreign payloads never land in your stream
- Design EventValue as a versioned enum (e.g. { V1(...), V2(...) }) so older readers keep decoding newer events
- Replay/verify with a unit test that BCS-encodes a sample event from the emitter and decodes it as the subscriber's EventValue
Example fix
// before: raw struct that breaks on any field change
#[derive(Serialize, Deserialize)]
struct EventValue { from: AccountOwner, amount: Amount }
// after: versioned enum, old readers stay compatible
#[derive(Serialize, Deserialize)]
enum EventValue {
V1 { from: AccountOwner, amount: Amount },
V2 { from: AccountOwner, amount: Amount, memo: String },
} Defensive patterns
Strategy: validation
Validate before calling
// Prove the emitter's schema decodes before subscribing.
#[test]
fn event_round_trip() {
let ev: EventValue = bcs::from_bytes(&emitter_sample_bytes()).unwrap();
assert_eq!(ev, sample_event());
} Prevention
- Namespace StreamNames with the application name so foreign payloads never reach your reader
- Version event payloads (enum V1/V2/...) so reader/writer upgrades stay decodable
- Rebuild and redeploy subscribers whenever the emitter's EventValue type changes
- Add a cross-module BCS round-trip test for every stream your app reads
When it happens
Trigger: Calling runtime.read_event(chain_id, name, index) for a stream produced by an application whose EventValue type differs from the Application::EventValue the reading contract was compiled with (different struct layout, enum variants, or field types).
Common situations: Upgrading the emitting application (new event fields) while subscribers still run the old module; two applications accidentally sharing the same StreamName with different payload types; copy-paste of a stream name across unrelated apps.
Related errors
- Failed to deserialize `Response` in cross-application call
- Checkpoint precondition failed: chain has consumed system ev
- Failed to deserialize service response
- Application parameters must be deserializable
- Failed to deserialize query response from application
AI-assisted analysis of linera-io/linera-protocol@6c226ddcb3 (2026-08-22).
Data as JSON: /api/errors/32093db2dc03daa8.
Report an issue: GitHub.