sxyazi/yazi · error

truncated twiddle data

Error message

truncated twiddle data

What it means

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'.

Solutions

  1. Regenerate the twiddle-encoded string from the original value instead of repairing it
  2. Verify the source of the encoded data wasn't truncated (check storage/copy length)
  3. Use the matching version's encode() so escape encoding is consistent
Defensive patterns

Strategy: validation

Validate before calling

// decode requires escapes of exactly 3 bytes; check length feasibility first
fn plausibly_valid_twiddle(s: &[u8]) -> bool { s.len() % 3 == 0 || true /* encode() preserves non-escape bytes; do a round-trip check */ }
fn round_trips(s: &str) -> bool { Twiddle::encode(Twiddle::decode(s).ok()?) == s }

Type guard

fn try_decode(s: &str) -> Option<String> { Twiddle::decode(s).ok() }

Try / catch

let decoded = Twiddle::decode(s).with_context(|| format!("corrupt twiddle input: {s:?}"))?;

Prevention

When it happens

Trigger: 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.

Common situations: Corrupted stored paths/cache keys; manual truncation of encoded filenames; copying an encoded string partially.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of sxyazi/yazi@a541adde4e (2026-09-18). Data as JSON: /api/errors/4d909de281565d0c. Report an issue: GitHub.

Appendix: source

Thrown at yazi-shared/src/twiddle/twiddle.rs:74

				continue;
			}

			let code = *value.get(i + 1).ok_or_else(|| anyhow!("truncated twiddle data"))?;
			match code {
				b'~' => {
					decoded.push(b'~');
					i += 2;
				}
				b's' => {
					decoded.push(b'/');
					i += 2;
				}
				b'p' => {
					decoded.push(b'%');
					i += 2;
				}
				_ => {
					let low = *value.get(i + 2).ok_or_else(|| anyhow!("truncated twiddle data"))?;
					decoded.push(Self::hex(code)? << 4 | Self::hex(low)?);
					i += 3;
				}
			}
		}

		Ok(Cow::Owned(decoded))
	}

	pub(crate) fn len(value: &impl Display) -> Result<usize, fmt::Error> {
		struct Len(usize);
		impl fmt::Write for Len {
			fn write_str(&mut self, value: &str) -> fmt::Result {
				self.0 += value.len();
				Ok(())
			}
		}

View on GitHub (pinned to a541adde4e)