gchq/CyberChef · error · OperationError
Unable to decode JSON payload: ${e.message}
Error message
Unable to decode JSON payload: ${e.message} What it means
Thrown after the signature has already validated successfully, when JSON.parse(payloadJson) fails on the decoded Base64 payload. This means the cookie is authentic but its payload is not valid JSON, which can happen with non-JSON serialization, encoding issues, or (rarely) a correctly-signed but corrupt payload. The original parse error message is appended for diagnostics.
Source
Thrown at src/core/operations/FlaskSessionVerify.mjs:129
throw new OperationError("Invalid signature!");
}
try {
const decoded = JSON.parse(payloadJson);
if (!args[3]) {
return {
valid: true,
payload: decoded,
};
} else {
return {
valid: true,
payload: decoded,
timestamp: timestamp
};
}
} catch (e) {
throw new OperationError("Unable to decode JSON payload: " + e.message);
}
}
}
export default FlaskSessionVerify;
View on GitHub (pinned to 4290ea7539)
Solutions
- Inspect the decoded payload bytes manually (decode parts[0] as URL-safe Base64) to see the actual content.
- If the app uses a custom serializer, decode the payload with that serializer instead of JSON.parse.
- Confirm the cookie was produced by Flask's default session interface and not a third-party signer.
- Check for double-encoding or transport corruption of the cookie value.
Example fix
// before: relying on JSON.parse of a custom-serialized payload
const decoded = JSON.parse(payloadJson); // throws
// after: inspect raw bytes first
const raw = Buffer.from(parts[0].replace(/-/g,'+').replace(/_/g,'/'), 'base64').toString('utf8');
console.log(raw); // determine actual format before parsing Defensive patterns
Strategy: try-catch
Validate before calling
// After signature passes, check the payload parses as JSON before relying on it
const raw = Buffer.from(parts[0].replace(/-/g,'+').replace(/_/g,'/'),'base64').toString('utf8');
let payload;
try { payload = JSON.parse(raw); } catch { /* not JSON; inspect raw */ } Type guard
function isJsonString(s) {
try { JSON.parse(s); return true; } catch { return false; }
} Try / catch
try {
const result = flaskVerify.run(cookie, args);
} catch (e) {
if (e.type === 'OperationError' && /Unable to decode JSON payload/.test(e.message)) {
// authentic cookie but non-JSON payload; decode manually with the app's serializer
} else throw e;
} Prevention
- Know whether the source Flask app uses the default JSON serializer or a custom one.
- Decode the payload bytes manually first if the serializer is non-standard.
When it happens
Trigger: Signature passes but the decoded payload bytes are not a JSON document; payload was serialized with a custom Flask JSON provider (e.g. compact/non-standard); the Base64 decoding produced mojibake due to a padding/encoding edge case; or an itsdangerous serializer other than the default JSON serializer was used.
Common situations: Flask app configured with a custom JSON serializer (e.g. TaggedJSONSerializer producing non-raw-JSON bytes); legacy itsdangerous versions with different defaults; payload containing binary that survived signing but is not UTF-8 JSON.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Unable to decode JSON payload: ${e.message}
- Invalid Base64 payload
- Invalid signature!
- No "r" value in the signature JSON
- No "s" value in the signature JSON
AI-assisted analysis of gchq/CyberChef@4290ea7539 (2026-08-13).
Data as JSON: /api/errors/411bc394232e6125.
Report an issue: GitHub.