Pumpkin-MC/Pumpkin · error · LoginError
JWT chain validation failed
Error message
JWT chain validation failed: {0} What it means
This is the LoginError::ChainValidationFailed variant of Pumpkin's Bedrock login error enum. It wraps an AuthError produced while validating the client's JWT identity chain during the Bedrock edition login handshake. The library throws it because the Xbox Live / Mojang certificate chain sent in the Login packet failed cryptographic or structural validation, so the client's identity cannot be trusted.
Solutions
- Have the client sign out and back into Xbox Live so a fresh, unexpired JWT chain is issued
- Update the server (and its bundled Mojang/Xbox public keys) so the trusted key set matches current Mojang certificates
- If the client is modified, revert to a vanilla client that presents a genuine certificate chain
- Check middleware/proxies between client and server for packet corruption of the login payload
Example fix
// before: accepting any login without inspecting the wrapped cause
match login_result { Err(e) => warn!("login failed"), }
// after: log the underlying AuthError to see why the chain was rejected
match login_result {
Err(LoginError::ChainValidationFailed(auth_err)) => {
warn!("JWT chain rejected: {auth_err}; client should re-authenticate with Xbox Live");
}
Err(e) => warn!("login failed: {e}"),
Ok(_) => {}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Before treating login as success, verify chain presence and freshness client-side
fn precheck_chain(jwt_chain: &[String]) -> Result<(), String> {
if jwt_chain.is_empty() { return Err("empty JWT chain".into()); }
if jwt_chain.iter().any(|j| j.is_empty()) { return Err("blank JWT in chain".into()); }
Ok(())
} Type guard
fn is_chain_validation_failed(e: &LoginError) -> bool {
matches!(e, LoginError::ChainValidationFailed(_))
} Try / catch
match login_result {
Err(LoginError::ChainValidationFailed(auth)) => {
// transient auth problems: prompt re-login; do not retry the same packet
disconnect_with("Please re-authenticate with Xbox Live and reconnect")
}
Err(e) => disconnect_with(&format!("Login failed: {e}")),
Ok(p) => admit(p),
} Prevention
- Keep clients signed into Xbox Live so tokens are fresh
- Keep the server updated with current Mojang trusted keys
- Avoid MITM proxies that alter the login payload
- Log the wrapped AuthError to distinguish expired vs untrusted signatures
When it happens
Trigger: A Bedrock client sends a Login packet whose JWT chain (xbox live -> xsts -> mojang certificates) fails AuthError validation — e.g. an expired XSTS token, a chain with missing or reordered links, a signature that doesn't verify, or a chain signed by an untrusted key.
Common situations: Clients with stale Xbox Live sessions, modified/unofficial clients presenting forged certificates, servers whose trusted root keys are outdated after a Mojang key rotation, or proxy setups that strip or mangle the chain.
Related errors
- Cannot accept self-signed token. Authentication is enforced…
- The validated username is invalid
- Could not parse UUID from validated token
- Got a guest/splitscreen login request. Currently…
- Failed to verify Ed25519 signature
AI-assisted analysis of Pumpkin-MC/Pumpkin@8d4639e25a (2026-09-09).
Data as JSON: /api/errors/bdfc0d44f36b75b1.
Report an issue: GitHub.
Appendix: source
Thrown at crates/pumpkin/src/net/bedrock/login/mod.rs:34
};
use pumpkin_protocol::bedrock::{
client::{resource_pack_stack::PackInstanceId, resource_packs_info::PackInfoData},
server::{login::ClientData, resource_pack_client_response::SResourcePackClientResponse},
};
use pumpkin_util::version::BedrockMinecraftVersion;
use pumpkin_world::{CURRENT_BEDROCK_MC_PROTOCOL, CURRENT_BEDROCK_MC_VERSION};
use serde::{Deserialize, de::Error};
use serde_repr::Deserialize_repr;
use std::sync::Arc;
use thiserror::Error;
use tracing::debug;
use uuid::Uuid;
#[derive(Debug, Error)]
pub enum LoginError {
#[error("Login packet data is not valid JSON")]
InvalidTokenFormat(#[from] serde_json::Error),
#[error("JWT chain validation failed: {0}")]
ChainValidationFailed(#[from] AuthError),
#[error("The validated username is invalid")]
InvalidUsername,
#[error("Could not parse UUID from validated token")]
InvalidUuid,
#[error("Cannot accept self-signed token. Authentication is enforced by server config.")]
SelfSignedNotAllowed,
#[error("Got a guest/splitscreen login request. Currently unimplemented.")]
GuestUnimplemented,
#[error("Failed to decode extra using decode_b64_url_nopad.")]
DecodeExtraError,
}
#[derive(Deserialize_repr)]
#[repr(u8)]
enum AuthenticationType {
Full,
Guest,View on GitHub (pinned to 8d4639e25a)