mgth/LittleBigMouse · error · CryptographicException
Not a protected payload.
Error message
Not a protected payload.
What it means
SecretProtector.Unprotect decrypts envelope strings that were produced by Protect and therefore start with a known Prefix. If the input does not carry that prefix, IsProtected returns false and the method throws this CryptographicException instead of attempting to parse plaintext. Callers are expected to treat it as 'not encrypted yet' and start fresh, per the XML docs.
Solutions
- Call IsProtected(envelope) before Unprotect and skip/decrypt accordingly, treating plaintext as already-unprotected.
- Check the stored value actually includes the prefix (e.g. starts with 'enc:' or whatever Prefix is) and re-encrypt it with Protect.
- Re-enter the secret in the app so it is saved in the current protected envelope format.
- Wrap Unprotect in try-catch for CryptographicException and fall back to treating the value as plaintext.
Example fix
// before
var secret = protector.Unprotect(storedValue);
// after
var secret = SecretProtector.IsProtected(storedValue)
? protector.Unprotect(storedValue)
: storedValue; Defensive patterns
Strategy: type-guard
Validate before calling
// only unprotect envelopes that actually carry the prefix
if (!SecretProtector.IsProtected(envelope))
return envelope; // already plaintext Type guard
static bool IsProtectedEnvelope(string? s) =>
!string.IsNullOrEmpty(s) && s.StartsWith(SecretProtector.Prefix, StringComparison.Ordinal); Try / catch
try
{
secret = protector.Unprotect(envelope);
}
catch (CryptographicException)
{
secret = envelope; // treat as plaintext and start fresh
} Prevention
- Always round-trip secrets through Protect/Unprotect; never splice envelope strings manually.
- Check IsProtected before calling Unprotect when values may predate encryption.
- When migrating config versions, re-Protect every value read in the old format.
When it happens
Trigger: Calling Unprotect on a plain (never-encrypted) string, on a value stored by an older app version before encryption was introduced, or on a value that lost its prefix through truncation/transformation.
Common situations: Upgrading from a pre-encryption version whose settings file holds plaintext secrets, manually copying a secret without its envelope prefix, or a config migration stripping the prefix.
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
- Protected payload has no scheme.
- Protected payload is truncated.
- No key at to read this payload with.
- Payload was protected with
- Unexpected token for a border resistance side.
AI-assisted analysis of mgth/LittleBigMouse@7a42f01d47 (2026-09-16).
Data as JSON: /api/errors/4a8cd1cd580efb68.
Report an issue: GitHub.
Appendix: source
Thrown at LittleBigMouse.Plugins/LittleBigMouse.Plugins.Core/SecretProtector.cs:88
using var aes = new AesGcm(GetOrCreateKey(), TagLength);
aes.Encrypt(
nonce,
bytes,
payload.AsSpan(NonceLength + TagLength),
payload.AsSpan(NonceLength, TagLength));
return Prefix + AesGcmScheme + "." + Convert.ToBase64String(payload);
}
/// <summary>
/// Reverses <see cref="Protect"/>. Throws — on a foreign scheme, a wrong key, a truncated
/// or tampered payload — rather than returning something plausible; callers treat that the
/// same way they already treat unreadable settings, by starting fresh.
/// </summary>
public string Unprotect(string envelope)
{
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.");
View on GitHub (pinned to 7a42f01d47)