{"record":{"id":"b168bdff18d4e634","repo":"astrid-runtime/astrid","slug":"too-many-metadata-mutations","errorCode":null,"errorMessage":"too many metadata mutations","messagePattern":"too many metadata mutations","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-storage/src/volume/hosted/mod.rs","lineNumber":500,"sourceCode":"                source,\n                destination,\n            } => {\n                if !regions.contains_key(destination) {\n                    return Err(invalid_transition(\"metadata replace destination is absent\"));\n                }\n                let source_state = regions\n                    .remove(source)\n                    .ok_or_else(|| invalid_transition(\"metadata replace source is absent\"))?;\n                regions.insert(destination.clone(), source_state);\n            },\n        }\n    }\n    Ok(())\n}\n\nfn encode_metadata_mutations(mutations: &[VolumeMetadataMutation]) -> io::Result<Vec<u8>> {\n    let count = u16::try_from(mutations.len())\n        .map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, \"too many metadata mutations\"))?;\n    let mut output = Vec::new();\n    output.extend_from_slice(&count.to_le_bytes());\n    for mutation in mutations {\n        let (kind, source, destination) = match mutation {\n            VolumeMetadataMutation::Rename {\n                source,\n                destination,\n            } => (1_u8, source, destination),\n            VolumeMetadataMutation::Replace {\n                source,\n                destination,\n            } => (2_u8, source, destination),\n        };\n        output.push(kind);\n        for region in [source, destination] {\n            let bytes = region.as_str().as_bytes();\n            let length = u16::try_from(bytes.len()).map_err(|_| {\n                io::Error::new(io::ErrorKind::InvalidInput, \"metadata region name too long\")","sourceCodeStart":482,"sourceCodeEnd":518,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-storage/src/volume/hosted/mod.rs#L482-L518","documentation":"encode_metadata_mutations serializes the mutation count as a single little-endian u16, so a transaction is hard-capped at 65535 entries. commit_metadata already rejects >1024, so this error means the encoding guard caught a batch that slipped past that validation (or the code was called directly).","triggerScenarios":"Calling encode_metadata_mutations with a slice longer than u16::MAX (65535); calling the encoder directly with an oversized batch, bypassing commit_metadata's 1024-mutation check.","commonSituations":"Directly invoking the internal encoder in tests or tooling; a future change lowering MAX_METADATA_MUTATIONS consistency or raising it above 65535 so the encoder overflows.","solutions":["Route all metadata commits through commit_metadata, which enforces the 1..=1024 bound before encoding.","Split the mutation list into batches of at most 1024 before encoding.","Add a length assertion in tests if you call the encoder directly."],"exampleFix":"// before\nlet bytes = encode_metadata_mutations(&huge_batch)?;\n// after\nfor chunk in huge_batch.chunks(1024) {\n    let bytes = encode_metadata_mutations(chunk)?;\n    /* write */\n}","handlingStrategy":"validation","validationCode":"fn encodable(mutations: &[VolumeMetadataMutation]) -> bool {\n    mutations.len() <= u16::MAX as usize\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always go through commit_metadata so the 1024 cap is enforced before encoding.","Chunk large batches before encoding.","Add a unit test asserting MAX_METADATA_MUTATIONS <= u16::MAX."],"tags":["storage","serialization","metadata","overflow"],"backgroundTag":"value-out-of-range","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}