EpicGames/lore · error
PresignTokenPayload is always serializable
Error message
PresignTokenPayload is always serializable
What it means
sign serializes a PresignTokenPayload with serde_json::to_string and panics via .expect because the payload struct is a plain serializable data type, so serialization is an invariant. The panic indicates the payload somehow failed to serialize — practically impossible unless the type definition changed (e.g. gained a non-serializable field or a custom Serialize impl that errors).
Solutions
- Inspect the most recent changes to PresignTokenPayload and revert/remove any field whose Serialize impl can fail
- Ensure all payload fields are plain data (strings, numbers, u8 version) with derived Serialize
- Run the round_trip_succeeds test to confirm sign/verify still work after changes
- If a fallible field is truly needed, switch sign to return Result<String, serde_json::Error> instead of expect
Example fix
// before
pub struct PresignTokenPayload { exp: u64, sub: String, weird: serde_json::Value /* may hold non-string-key maps */ }
// after
#[derive(Serialize, Deserialize)]
pub struct PresignTokenPayload { version: u8, expires_at: u64, subject: String } Defensive patterns
Strategy: try-catch
Type guard
fn is_plain_payload(p: &PresignTokenPayload) -> bool {
// derived Serialize on a struct of plain fields guarantees success; check no exotic fields added
serde_json::to_string(p).is_ok()
} Try / catch
match serde_json::to_string(payload) {
Ok(json) => { /* proceed with hmac sign */ },
Err(e) => log::error!("PresignTokenPayload failed to serialize after refactor: {e}"),
} Prevention
- Keep PresignTokenPayload limited to derived-Serialize plain fields
- Add a serialization round-trip unit test (round_trip_succeeds) to CI
- Avoid adding serde_json::Value or map fields with non-string keys
- If fallibility ever becomes real, convert sign to return Result instead of expect
When it happens
Trigger: Only reachable if PresignTokenPayload's Serialize impl returns Err — e.g. someone added a field with a serializer that fails, a map with non-string keys, or serde_json was built without required features. Normal use of sign with the current struct cannot panic here.
Common situations: Appears after refactors where the payload type gained a non-serializable member, or in tests constructing exotic payloads via generic helpers; essentially a compile-time-guaranteed invariant being violated by a code change.
Understand the failure class
Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.
Related errors
- Failed to open log file
- {e}
- Networking not supported on this OS
- could not get available parallelism
- hex encode failed
AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13).
Data as JSON: /api/errors/01df2fb787ceb8ba.
Report an issue: GitHub.
Appendix: source
Thrown at lore-server/src/http/presign_token.rs:44
#[derive(Debug, Error, PartialEq)]
pub enum PresignTokenError {
#[error("invalid token format")]
InvalidFormat,
#[error("invalid token signature")]
InvalidSignature,
#[error("unknown token version: {0}")]
UnknownVersion(u8),
#[error("token was signed by a different key")]
KeyIdMismatch,
#[error("token has expired")]
Expired,
}
pub const CURRENT_TOKEN_VERSION: u8 = 1;
/// Signs `payload` and returns `<base64url(json)>.<base64url(signature)>`.
pub fn sign(payload: &PresignTokenPayload, key: &hmac::Key) -> String {
let json = serde_json::to_string(payload).expect("PresignTokenPayload is always serializable");
let encoded_payload = URL_SAFE_NO_PAD.encode(json.as_bytes());
let signature = hmac::sign(key, encoded_payload.as_bytes());
let encoded_sig = URL_SAFE_NO_PAD.encode(signature.as_ref());
format!("{encoded_payload}.{encoded_sig}")
}
/// Verifies a token and returns the payload if valid.
///
/// Checks (in order): format, signature, version, `key_id`, expiry.
pub fn verify(
token: &str,
key: &hmac::Key,
key_id: &str,
now_unix: u64,
) -> Result<PresignTokenPayload, PresignTokenError> {
let (encoded_payload, encoded_sig) = token
.split_once('.')
.ok_or(PresignTokenError::InvalidFormat)?;View on GitHub (pinned to 074eb0b0d1)