mgth/LittleBigMouse · error · CryptographicException
Protected payload is truncated.
Error message
Protected payload is truncated.
What it means
For AES-GCM envelopes, the payload must contain at least NonceLength bytes of nonce plus TagLength bytes of authentication tag, followed by the ciphertext. If the base64-decoded payload is shorter than that combined minimum, decryption is impossible and this CryptographicException is thrown. It almost always means the encrypted payload was truncated or corrupted.
Solutions
- Re-encrypt the secret with SecretProtector.Protect to regenerate a complete nonce+ciphertext+tag payload.
- Verify the stored envelope is complete — re-copy the whole string with no truncation and re-check the base64 decodes cleanly.
- Delete the unreadable stored value and re-enter the secret so the app saves a fresh valid envelope.
- Catch CryptographicException during settings load and fall back to prompting the user for the secret again.
Example fix
// before (truncated payload)
var secret = protector.Unprotect(storedTruncatedEnvelope);
// after
try { var secret = protector.Unprotect(storedEnvelope); }
catch (CryptographicException) { secret = PromptUserForSecret(); } Defensive patterns
Strategy: try-catch
Validate before calling
// rough pre-check: decoded payload must exceed nonce+tag length
var body = envelope.Substring(SecretProtector.Prefix.Length);
var payload = Convert.FromBase64String(body[(body.IndexOf('.') + 1)..]);
if (payload.Length < SecretProtector.NonceLength + SecretProtector.TagLength)
throw new FormatException("Payload too short to contain nonce and tag."); Type guard
static bool PayloadIsPlausible(string envelope, string prefix)
{
try
{
var body = envelope.AsSpan(prefix.Length);
var dot = body.IndexOf('.');
if (dot < 0) return false;
var payload = Convert.FromBase64String(body[(dot + 1)..].ToString());
return payload.Length >= SecretProtector.NonceLength + SecretProtector.TagLength;
}
catch (FormatException) { return false; }
} Try / catch
try
{
secret = protector.Unprotect(envelope);
}
catch (CryptographicException ex) when (ex.Message.Contains("truncated"))
{
Log.LogWarning("Encrypted payload truncated; prompting for secret again.");
secret = PromptUserForSecret();
} Prevention
- Copy/store encrypted envelopes as whole strings; truncation makes them undecryptable.
- Keep nonce and tag lengths consistent across app versions or migrate old payloads explicitly.
- On load failure, treat the secret as lost and re-prompt rather than retrying decryption.
When it happens
Trigger: Calling Unprotect on an AES-GCM envelope whose decoded payload is shorter than NonceLength + TagLength — e.g. a truncated settings value, a partially copied string, or a payload produced with different nonce/tag sizes by another version.
Common situations: Secret cut off during manual copy/paste, settings file corruption, or an app/config version change that altered nonce or tag lengths so old payloads no longer meet the size minimum.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- Not a protected payload.
- Protected payload has no scheme.
- No key at to read this payload with.
- Unexpected token for a border resistance side.
- Payload was protected with
AI-assisted analysis of mgth/LittleBigMouse@7a42f01d47 (2026-09-16).
Data as JSON: /api/errors/d4f94b3a4330f4bb.
Report an issue: GitHub.
Appendix: source
Thrown at LittleBigMouse.Plugins/LittleBigMouse.Plugins.Core/SecretProtector.cs:105
if (!IsProtected(envelope))
throw new CryptographicException("Not a protected payload.");
var body = envelope.AsSpan(Prefix.Length);
var separator = body.IndexOf('.');
if (separator < 0) throw new CryptographicException("Protected payload has no scheme.");
var scheme = body[..separator].ToString();
var payload = Convert.FromBase64String(body[(separator + 1)..].ToString());
switch (scheme)
{
case DpapiScheme when OperatingSystem.IsWindows():
return Encoding.UTF8.GetString(
ProtectedData.Unprotect(payload, null, DataProtectionScope.CurrentUser));
case AesGcmScheme:
if (payload.Length < NonceLength + TagLength)
throw new CryptographicException("Protected payload is truncated.");
var plain = new byte[payload.Length - NonceLength - TagLength];
using (var aes = new AesGcm(ReadKey() ?? throw new CryptographicException(
$"No key at {_keyFilePath} to read this payload with."), TagLength))
{
aes.Decrypt(
payload.AsSpan(0, NonceLength),
payload.AsSpan(NonceLength + TagLength),
payload.AsSpan(NonceLength, TagLength),
plain);
}
return Encoding.UTF8.GetString(plain);
default:
throw new CryptographicException(
$"Payload was protected with '{scheme}', unreadable on this system.");
}
}View on GitHub (pinned to 7a42f01d47)