{"record":{"id":"62d227917a04c026","repo":"linera-io/linera-protocol","slug":"keccak-256-tag-collision-between-variants-other","errorCode":null,"errorMessage":"Keccak-256 tag collision between variants `{other_name}` and `{name}` (tag = {tag:#010x}). Rename one of the variants.","messagePattern":"Keccak-256 tag collision between variants `(.+?)` and `(.+?)` \\(tag = (.+?)\\)\\. Rename one of the variants\\.","errorType":"validation","errorClass":"syn::Error","httpStatus":null,"severity":"error","filePath":"linera-sdk-derive/src/stable_enum.rs","lineNumber":74,"sourceCode":"\n/// Computes the stable tag for a variant name.\nfn compute_tag(variant_name: &str) -> u32 {\n    let hash = Keccak256::digest(variant_name.as_bytes());\n    let val = u32::from_be_bytes([hash[0], hash[1], hash[2], hash[3]]);\n    (val & 0x07FF_FFFF) | 0x0800_0000\n}\n\n/// Computes all variant tags, returning an error on a (vanishingly unlikely) collision.\nfn variant_tags(input: &ItemEnum) -> Result<Vec<(String, u32, &Variant)>> {\n    let mut out = Vec::with_capacity(input.variants.len());\n    for variant in &input.variants {\n        let name = variant.ident.to_string();\n        let tag = compute_tag(&name);\n        if let Some((other_name, _, _)) = out\n            .iter()\n            .find(|(_, t, _): &&(String, u32, &Variant)| *t == tag)\n        {\n            return Err(Error::new(\n                variant.span(),\n                format!(\n                    \"Keccak-256 tag collision between variants `{other_name}` and `{name}` \\\n                     (tag = {tag:#010x}). Rename one of the variants.\"\n                ),\n            ));\n        }\n        out.push((name, tag, variant));\n    }\n    Ok(out)\n}\n\nfn reject_generics(input: &ItemEnum) -> Result<()> {\n    if !input.generics.params.is_empty() || input.generics.where_clause.is_some() {\n        return Err(Error::new(\n            input.generics.span(),\n            \"#[derive(StableEnum)] does not yet support generic enums\",\n        ));","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/linera-io/linera-protocol/blob/6c226ddcb332ef55118dc8d0aafbd093d5420899/linera-sdk-derive/src/stable_enum.rs#L56-L92","documentation":"`#[derive(StableEnum)]` assigns each enum variant a deterministic u32 tag: Keccak-256 of the variant name, first 4 bytes big-endian, masked into a 27-bit range so the ULEB128 encoding is always 4 bytes. At macro-expansion time `variant_tags` rejects duplicate tags, failing compilation with this error. With 2^27 possible tags the birthday bound makes collisions realistic only beyond roughly ten thousand variants — for hand-written enums this is essentially unreachable.","triggerScenarios":"Deriving `StableEnum` on an enum where two variant names hash to the same 27-bit tag; procedurally generated enums with low name diversity (Variant00001-style names) at scales of ~10^4 variants.","commonSituations":"Code-generated enums from external schemas with auto-numbered variant names; tests that deliberately exercise the collision path of the derive macro.","solutions":["Rename one of the two variants named in the error — the message lists both names and the shared tag value.","If names are generated, make the generator produce diverse, semantic names instead of sequential indices.","Re-run `cargo check` after renaming; tags are deterministic, so the same pair always collides until renamed."],"exampleFix":"// before\n#[derive(StableEnum)]\nenum Action { Transfer, Transferr } // hypothetical Keccak-256 tag collision\n\n// after\n#[derive(StableEnum)]\nenum Action { Transfer, TransferV2 } // renamed variant gets a fresh tag","handlingStrategy":"validation","validationCode":"// compile-time: a CI `cargo check` on every generated enum catches this before merge\n// `cargo check -p my-app` fails with the collision message listing both variant names","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Prefer semantic variant names over sequential generated ones.","Keep generated enums small; split large schemas into multiple enums.","Treat this as a deterministic compile error — the same pair always collides until renamed."],"tags":["proc-macro","stable-enum","compile-error","hash-collision","serde"],"backgroundTag":"derive-macro-error","analyzedSha":"6c226ddcb332ef55118dc8d0aafbd093d5420899","analyzedAt":"2026-08-22T22:49:09.787Z","schemaVersion":2},"datasetVersion":"2026-08-23T01:17:44.959Z"}