GraphiteEditor/Graphite · critical
Failed to serialize FrontendMessage
Error message
Failed to serialize FrontendMessage
What it means
Every `FrontendMessage` the Rust editor emits is converted to a JS value with `serde_wasm_bindgen` (with `serialize_large_number_types_as_bigints(true)`, so u64/i64 become JS `BigInt`) and handed to the JS callback registered at wrapper creation. The `.expect` fires when serialization returns `Err`, which happens for values that have no JS representation — classically non-string map keys (e.g. a `HashMap` keyed by a struct or tuple), unsupported number types, or a message variant whose payload type gained a field serde cannot map.
Source
Thrown at frontend/wrapper/src/editor_wrapper.rs:135
}
#[cfg(feature = "editor")]
pub(crate) fn send_frontend_message_to_js(&self, message: FrontendMessage) {
if let FrontendMessage::UpdateImageData { ref image_data } = message {
let new_hash = calculate_hash(image_data);
let prev_hash = IMAGE_DATA_HASH.load(Ordering::Relaxed);
if new_hash != prev_hash {
render_image_data_to_canvases(image_data.iter());
IMAGE_DATA_HASH.store(new_hash, Ordering::Relaxed);
}
return;
}
let message_type = message.to_discriminant().local_name();
let serializer = serde_wasm_bindgen::Serializer::new().serialize_large_number_types_as_bigints(true);
let message_data = message.serialize(&serializer).expect("Failed to serialize FrontendMessage");
let js_return_value = self.frontend_message_handler_callback.call2(&JsValue::null(), &JsValue::from(message_type), &message_data);
if let Err(error) = js_return_value {
error!("While handling FrontendMessage {:?}, JavaScript threw an error:\n{:?}", message.to_discriminant().local_name(), error,)
}
}
pub(crate) fn forward_serialized_frontend_message_to_js(&self, name: &str, data: crate::wasm_value::WasmValue) {
let js_return_value = self.frontend_message_handler_callback.call2(&JsValue::null(), &JsValue::from(name), &data.into());
if let Err(error) = js_return_value {
error!("While handling FrontendMessage {name:?}, JavaScript threw an error:\n{error:?}")
}
}
#[cfg(all(feature = "native", target_family = "wasm"))]
pub(crate) fn send(&self, command: EditorCommand) {View on GitHub (pinned to c507b35645)
Solutions
- Find the offending variant from the panic backtrace or by logging `message.to_discriminant().local_name()` just before serializing.
- Change message payloads to use string keys (`HashMap<String, _>` / `BTreeMap<String, _>`) or serialize as a Vec of pairs.
- Add a CI/test that serializes a sample of every `FrontendMessage` variant through the same `serde_wasm_bindgen::Serializer` configuration.
- Replace the `.expect` with a match that logs the discriminant and skips the message so one bad variant doesn't kill the editor.
Example fix
// before
let serializer = serde_wasm_bindgen::Serializer::new().serialize_large_number_types_as_bigints(true);
let message_data = message.serialize(&serializer).expect("Failed to serialize FrontendMessage");
// after
let serializer = serde_wasm_bindgen::Serializer::new().serialize_large_number_types_as_bigints(true);
let Ok(message_data) = message.serialize(&serializer) else {
error!("Failed to serialize FrontendMessage {:?}", message.to_discriminant().local_name());
return;
}; Defensive patterns
Strategy: fallback
Validate before calling
// Rust: CI guard proving every FrontendMessage serializes like the wrapper does
fn serializer() -> serde_wasm_bindgen::Serializer {
serde_wasm_bindgen::Serializer::new().serialize_large_number_types_as_bigints(true)
}
#[test]
fn frontend_messages_serialize() {
for message in sample_of_every_frontend_message() {
assert!(message.serialize(&serializer()).is_ok(),
"{:?} is not representable as a JS value", message.to_discriminant());
}
} Try / catch
JS cannot catch the wasm panic; after suspect operations check `await editor.hasCrashed()`. In the wrapper itself, match on serialize() and log-and-skip the message so one bad variant degrades instead of killing all frontend updates.
Prevention
- Use string keys in any map carried by a FrontendMessage.
- Test every message variant through serde_wasm_bindgen (not just serde_json) in CI.
- Handle BigInt payloads (u64 serialized with serialize_large_number_types_as_bigints) on the TS side.
When it happens
Trigger: Adding a `FrontendMessage` variant containing a map with non-string keys (struct/enum/tuple keys), a `usize`/`u128` field on a serializer path not covered by the bigints setting, or any `Serialize` impl that emits an unsupported construct; the first time that message is emitted after a user action, the wrapper panics instead of delivering data.
Common situations: Contributors adding new frontend message payloads during feature work; refactoring a keyed collection into a message (e.g. `HashMap<NodeId, …>` without converting keys to strings first); upgrading serde/serde_wasm_bindgen versions that change supported representations.
Related errors
- Failed to get canvas context
- Failed to get canvas context
- Failed to draw ellipse
- Failed to transform circle
- Failed to draw the circle
AI-assisted analysis of GraphiteEditor/Graphite@c507b35645 (2026-08-16).
Data as JSON: /api/errors/93e8aada2890bf89.
Report an issue: GitHub.