{"record":{"id":"7fd145b35873afad","repo":"leptos-rs/leptos","slug":"expected-a-number-string-tuple","errorCode":null,"errorMessage":"Expected a [number, string] tuple","messagePattern":"Expected a \\[number, string\\] tuple","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"hydration_context/src/hydrate.rs","lineNumber":43,"sourceCode":"    #[wasm_bindgen(thread_local)]\n    static __SERIALIZED_ERRORS: Array;\n\n    #[wasm_bindgen(thread_local)]\n    static __INCOMPLETE_CHUNKS: Array;\n}\n\nfn serialized_errors() -> Vec<(SerializedDataId, ErrorId, Error)> {\n    __SERIALIZED_ERRORS.with(|s| {\n        s.iter()\n            .flat_map(|value| {\n                value.dyn_ref::<Array>().map(|value| {\n                    let error_boundary_id =\n                        value.get(0).as_f64().unwrap() as usize;\n                    let error_id = value.get(1).as_f64().unwrap() as usize;\n                    let value = value\n                        .get(2)\n                        .as_string()\n                        .expect(\"Expected a [number, string] tuple\");\n                    (\n                        SerializedDataId(error_boundary_id),\n                        ErrorId::from(error_id),\n                        Error::from(SerializedError(value)),\n                    )\n                })\n            })\n            .collect()\n    })\n}\n\nfn incomplete_chunks() -> Vec<SerializedDataId> {\n    __INCOMPLETE_CHUNKS.with(|i| {\n        i.iter()\n            .map(|value| {\n                let id = value.as_f64().unwrap() as usize;\n                SerializedDataId(id)\n            })","sourceCodeStart":25,"sourceCodeEnd":61,"githubUrl":"https://github.com/leptos-rs/leptos/blob/32d20f6c9d517451d77beb68d88c7716823dac41/hydration_context/src/hydrate.rs#L25-L61","documentation":"During client-side hydration, `serialized_errors()` reads the `__SERIALIZED_ERRORS` JS array injected by the server and expects every entry to be a `[number, string]`-shaped triple where index 2 is a JS string carrying the serialized error. If element 2 is not a string (e.g. `as_string()` returns None), the `.expect` panics in the WASM client, breaking hydration.","triggerScenarios":"Calling hydration entry points that collect serialized errors (e.g. Leptos hydration on the client) when the injected error payload has a non-string third element — typically a mismatch between server-side serialization code and client-side deserialization, hand-written or older hydration data, or corrupted/transformed hydration markers (HTML rewriters, CDNs, proxies mangling the payload).","commonSituations":"Version mismatch between server (leptos/Legerder serialization) and client hydration crates; custom middleware that rewrites hydration markers; an Error/CustomError serialized as a structured object instead of a string; manually edited SSR output in tests or static snapshots.","solutions":["Ensure server and hydration_context/throw_error crates are the same version (align leptos/leptos_config and hydration_context versions in Cargo.toml)","Inspect the rendered HTML hydration markers and confirm each error entry is a [number, string] tuple; fix whatever produces object/array values at index 2","Disable or configure any HTML-rewriting middleware/CDN minification that could alter the serialized error data","If you construct test hydration data, serialize error messages with `.to_string()` so element 2 is always a JS string"],"exampleFix":"// before (custom error serialization on server)\nserde_json::to_value(my_error).unwrap() // object, not string\n// after\nmy_error.to_string() // plain string, matches [number, string] tuple expectation","handlingStrategy":"type-guard","validationCode":"// client-side, before trusting hydration data\nlet raw = js_sys::Reflect::get(&window(), &\"__SERIALIZED_ERRORS\".into()).ok();\nlet ok = raw.map(|v| {\n    js_sys::Array::from(&v).iter().all(|entry| {\n        let a = js_sys::Array::from(&entry);\n        a.length() >= 3 && a.get(2).is_string()\n    })\n}).unwrap_or(false);","typeGuard":"fn is_error_tuple(entry: &JsValue) -> bool {\n    entry\n        .dyn_ref::<js_sys::Array>()\n        .map(|a| a.length() >= 3 && a.get(2).is_string())\n        .unwrap_or(false)\n}","tryCatchPattern":"// wrap hydration bootstrap so a malformed payload degrades to client render\nmatch std::panic::catch_unwind(|| hydrate()) {\n    Ok(_) => {},\n    Err(_) => mount_to_body(|view| { /* full client-side render */ }),\n}","preventionTips":["Keep server and client hydration crates on identical versions","Never hand-edit hydration markers in SSR output or snapshots","Disable HTML rewriting/minification middleware that could corrupt hydration data","Serialize error payloads with .to_string() so element 2 is always a JS string"],"tags":["rust","wasm","hydration","serialization","ssr"],"backgroundTag":"hydration-payload-mismatch","analyzedSha":"32d20f6c9d517451d77beb68d88c7716823dac41","analyzedAt":"2026-09-01T18:55:42.580Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T01:17:15.007Z"}