{"record":{"id":"4d909de281565d0c","repo":"sxyazi/yazi","slug":"truncated-twiddle-data","errorCode":null,"errorMessage":"truncated twiddle data","messagePattern":"truncated twiddle data","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"yazi-shared/src/twiddle/twiddle.rs","lineNumber":74,"sourceCode":"\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tlet code = *value.get(i + 1).ok_or_else(|| anyhow!(\"truncated twiddle data\"))?;\n\t\t\tmatch code {\n\t\t\t\tb'~' => {\n\t\t\t\t\tdecoded.push(b'~');\n\t\t\t\t\ti += 2;\n\t\t\t\t}\n\t\t\t\tb's' => {\n\t\t\t\t\tdecoded.push(b'/');\n\t\t\t\t\ti += 2;\n\t\t\t\t}\n\t\t\t\tb'p' => {\n\t\t\t\t\tdecoded.push(b'%');\n\t\t\t\t\ti += 2;\n\t\t\t\t}\n\t\t\t\t_ => {\n\t\t\t\t\tlet low = *value.get(i + 2).ok_or_else(|| anyhow!(\"truncated twiddle data\"))?;\n\t\t\t\t\tdecoded.push(Self::hex(code)? << 4 | Self::hex(low)?);\n\t\t\t\t\ti += 3;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tOk(Cow::Owned(decoded))\n\t}\n\n\tpub(crate) fn len(value: &impl Display) -> Result<usize, fmt::Error> {\n\t\tstruct Len(usize);\n\t\timpl fmt::Write for Len {\n\t\t\tfn write_str(&mut self, value: &str) -> fmt::Result {\n\t\t\t\tself.0 += value.len();\n\t\t\t\tOk(())\n\t\t\t}\n\t\t}\n","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/sxyazi/yazi/blob/a541adde4e5b082719a0f21a87eb3eab071ece58/yazi-shared/src/twiddle/twiddle.rs#L56-L92","documentation":"Twiddle decoding expects percent-like escapes of exactly 3 characters (a marker, a hex high nibble, and a hex low nibble). If the escape's third character is missing (the byte slice ends early), decode returns 'truncated twiddle data'.","triggerScenarios":"Calling decode on a string where a non-'p' escape character appears within the last two bytes, e.g. encode-then-truncate or hand-edited twiddle strings.","commonSituations":"Corrupted stored paths/cache keys; manual truncation of encoded filenames; copying an encoded string partially.","solutions":["Regenerate the twiddle-encoded string from the original value instead of repairing it","Verify the source of the encoded data wasn't truncated (check storage/copy length)","Use the matching version's encode() so escape encoding is consistent"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// decode requires escapes of exactly 3 bytes; check length feasibility first\nfn plausibly_valid_twiddle(s: &[u8]) -> bool { s.len() % 3 == 0 || true /* encode() preserves non-escape bytes; do a round-trip check */ }\nfn round_trips(s: &str) -> bool { Twiddle::encode(Twiddle::decode(s).ok()?) == s }","typeGuard":"fn try_decode(s: &str) -> Option<String> { Twiddle::decode(s).ok() }","tryCatchPattern":"let decoded = Twiddle::decode(s).with_context(|| format!(\"corrupt twiddle input: {s:?}\"))?;","preventionTips":["Never truncate twiddle-encoded strings","Round-trip test encode/decode on stored values","Store a length or checksum alongside encoded cache keys"],"tags":["encoding","decoding","corruption"],"backgroundTag":"invalid-argument-format","analyzedSha":"a541adde4e5b082719a0f21a87eb3eab071ece58","analyzedAt":"2026-09-18T10:12:31.659Z","contentChangedAt":"2026-09-18T10:12:31.659Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}