{"record":{"id":"4927ddbeaca0f7b2","repo":"linera-io/linera-protocol","slug":"attempt-to-unflatten-an-invalid-char","errorCode":null,"errorMessage":"Attempt to unflatten an invalid `char`","messagePattern":"Attempt to unflatten an invalid `char`","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"linera-witty/src/primitive_types/simple_type.rs","lineNumber":71,"sourceCode":"                unflatten(flat)\n            }\n        }\n    };\n}\n\nsimple_type!(bool -> i32, 1, { |flat| flat != 0 });\nsimple_type!(i8 -> i32, 1);\nsimple_type!(i16 -> i32, 2);\nsimple_type!(i32 -> i32, 4);\nsimple_type!(i64 -> i64, 8);\nsimple_type!(u8 -> i32, 1);\nsimple_type!(u16 -> i32, 2);\nsimple_type!(u32 -> i32, 4);\nsimple_type!(u64 -> i64, 8);\nsimple_type!(f32 -> f32, 4);\nsimple_type!(f64 -> f64, 8);\nsimple_type!(char -> i32, 4,\n    { |flat| char::from_u32(flat as u32).expect(\"Attempt to unflatten an invalid `char`\") }\n);\n","sourceCodeStart":53,"sourceCodeEnd":73,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-witty/src/primitive_types/simple_type.rs#L53-L73","documentation":"A WIT `char` crosses the boundary as an i32 flat value holding the Unicode code point. Unflattening calls `char::from_u32(flat as u32)`, which returns None for values that are not Unicode scalar values — the surrogate range 0xD800-0xDFFF and anything above 0x10FFFF — and `.expect` panics. Receiving such a value means the guest produced a code point that Rust `char`s cannot represent, violating the WIT type contract.","triggerScenarios":"A guest passes 0xD800..=0xDFFF or >0x10FFFF where the WIT interface declares char; the guest builds its 'char' from raw u32 bits (e.g. UTF-16 surrogate pieces) without validation.","commonSituations":"Guests ported from C/JS that treat chars as plain 32-bit ints; incorrect UTF-16 surrogate-pair handling that forwards lone surrogates; fuzz tests feeding arbitrary u32 into char-typed entry points.","solutions":["Validate in the guest before passing/returning a char: `char::from_u32(v).ok_or(Error::InvalidCodePoint)`","Fix surrogate handling: recombine UTF-16 surrogate pairs into one scalar value; never forward a lone surrogate","If arbitrary u32 values must cross, declare the parameter as u32 in WIT and validate/convert to char on the host side"],"exampleFix":"// before (guest): forwards a raw code point unchecked\nfn symbol(idx: u32) -> char {\n    let code = table_lookup(idx); // may be 0xD800..=0xDFFF\n    unsafe { char::from_u32_unchecked(code) } // host panics on unflatten\n}\n\n// after (guest): validate at the boundary\nfn symbol(idx: u32) -> Result<char, InvalidChar> {\n    char::from_u32(table_lookup(idx)).ok_or(InvalidChar)\n}","handlingStrategy":"type-guard","validationCode":"// guest: assert boundary inputs/outputs are valid scalar values\nassert!(char::from_u32(code).is_some(), \"invalid code point {code:#x}\");","typeGuard":"fn is_valid_scalar(code: u32) -> bool { char::from_u32(code).is_some() }","tryCatchPattern":null,"preventionTips":["Use char::from_u32 (returns Option) instead of from_u32_unchecked or integer casts","Never pass UTF-16 surrogates across the WIT boundary; recombine pairs first","Fuzz char-typed entry points with valid scalar values (0..=0x10FFFF minus surrogates)"],"tags":["wasm","wit","unicode","char","linera-witty"],"backgroundTag":"invalid-unicode-code-point","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}