yewstack/yew · error

failed to deserialize state

Error message

failed to deserialize state

What it means

With use_prepared_state, the server serializes state and embeds it base64-encoded in the HTML; during hydration the client rebuilds a data:application/octet-binary;base64,... URL, fetch()es it, and decodes the bytes (decode_base64 in feat_hydration.rs). This expect fires at that first step - the fetch/decode of the data URL fails. Since Yew itself generated the payload, a failure means the HTML was altered between SSR and hydration (attributes stripped, re-escaped, or truncated) or the environment blocks data: fetches.

Source

Thrown at packages/yew/src/functional/hooks/use_prepared_state/feat_hydration.rs:91

            let data = use_state(|| {
                let (s, handle) = Suspension::new();
                (
                    SuspensionResult::<(Option<Rc<T>>, Option<Rc<D>>)>::Err(s),
                    Some(handle),
                )
            })
            .run(ctx);

            let state = {
                let data = data.clone();
                ctx.next_prepared_state(move |_re_render, buf| -> PreparedStateBase<T, D> {
                    if let Some(buf) = buf {
                        let buf = format!("data:application/octet-binary;base64,{buf}");

                        spawn_local(async move {
                            let buf = decode_base64(&buf)
                                .await
                                .expect("failed to deserialize state");

                            let ((state, deps), _) =
                                bincode::serde::decode_from_slice::<(Option<T>, Option<D>), _>(
                                    &buf,
                                    bincode::config::standard(),
                                )
                                .map(|((state, deps), consumed)| {
                                    ((state.map(Rc::new), deps.map(Rc::new)), consumed)
                                })
                                .expect("failed to deserialize state");

                            data.set((Ok((state, deps)), None));
                        });
                    }

                    PreparedStateBase {
                        #[cfg(feature = "ssr")]
                        state: None,

View on GitHub (pinned to 0e4a05472f)

Solutions

  1. View the served page source and confirm the attribute containing application/octet-binary;base64 is present and intact
  2. Allow data: in the CSP directives governing fetch (e.g. connect-src data:) or restructure how state is transferred
  3. Disable attribute minification/re-escaping in the HTML pipeline between SSR and hydration
  4. Purge cached SSR HTML after each deploy so server output and client bundle always come from the same build
Defensive patterns

Strategy: validation

Validate before calling

// Before hydrate(), confirm the serialized state survived your HTML pipeline:
let html = gloo::utils::document()
    .document_element()
    .expect("no document element")
    .outer_html();
assert!(
    html.contains("application/octet-binary;base64"),
    "prepared-state marker was stripped by HTML post-processing"
);

Prevention

When it happens

Trigger: Hydrating HTML whose embedded base64 state attribute was mangled by a minifier or template engine that re-escapes attributes; a Content-Security-Policy that does not allow data: URLs for fetch; proxies or CDNs truncating very long attributes; cached SSR HTML that no longer matches the deployed client bundle.

Common situations: Post-processing SSR output (minify/sanitize steps) between server and client; strict CSP rollouts; edge caches serving stale pages across deploys; intermediaries rewriting data: URLs.

Related errors


AI-assisted analysis of yewstack/yew@0e4a05472f (2026-08-22). Data as JSON: /api/errors/35b30e1975657ef6. Report an issue: GitHub.