EpicGames/lore · error · anyhow::Error
Failed to decode protocol
Error message
Failed to decode protocol: {e:?} What it means
The negotiated ALPN protocol bytes from the TLS handshake must be valid UTF-8; quinn stores them as raw bytes. If they are not, String::from_utf8 fails and get_protocol wraps the error with this message.
Solutions
- Fix the client to send ASCII/UTF-8 ALPN values
- Use String::from_utf8_lossy or bytes::String where possible to tolerate invalid input
- Keep ALPN registrations to standard ASCII protocol names
Example fix
// before
.map(String::from_utf8)
.transpose()
.map_err(|e| anyhow!("Failed to decode protocol: {e:?}"))?
// after
.map(|b| String::from_utf8_lossy(&b).into_owned()) Defensive patterns
Strategy: validation
Validate before calling
// validate ALPN values at registration time
fn valid_alpn(p: &[u8]) -> bool { std::str::from_utf8(p).map(|s| s.is_ascii()).unwrap_or(false) } Try / catch
match get_protocol(&connection) {
Err(e) if e.to_string().contains("Failed to decode protocol") => { warn!("invalid UTF-8 ALPN from client"); return Ok(()); }
r => r?,
} Prevention
- Only register ASCII ALPN protocol names
- Consider from_utf8_lossy for tolerant decoding
- Reject non-ASCII ALPNs at TLS config build time
When it happens
Trigger: A client (or middlebox) sends an ALPN value containing invalid UTF-8 bytes; the protocol field decodes as Some(bytes) but from_utf8 returns Err.
Common situations: Malicious or buggy clients probing the server; custom ALPN strings with non-ASCII characters; corrupted handshake data.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- No alpns provided
- No protocol found on request
- Received connection for unsupported protocol
- Stream handler factory was not set
- Address was not set
AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13).
Data as JSON: /api/errors/19b32abd6a8ea082.
Report an issue: GitHub.
Appendix: source
Thrown at lore-server/src/quic/quinn/quinn_server.rs:261
.in_current_span(),
)
);
}
Ok(())
}
fn get_protocol(connection: &quinn::Connection) -> Result<String, anyhow::Error> {
let handshake_data = connection
.handshake_data()
.and_then(|h| h.downcast::<HandshakeData>().ok())
.ok_or(anyhow!("No handshake data"))?;
handshake_data
.protocol
.map(String::from_utf8)
.transpose()
.map_err(|e| anyhow!("Failed to decode protocol: {e:?}"))?
.ok_or(anyhow!("No protocol found on request"))
}
#[tracing::instrument(
name = "urc-quic",
skip_all,
fields(connection_id, protocol, correlation_id, repository_id)
)]
async fn handle_conn(
conn: quinn::Incoming,
monitor: TaskMonitor,
connection_metrics_interval: Duration,
stream_handler_factory: Arc<Box<dyn StreamHandlerFactory>>,
) -> anyhow::Result<()> {
let connection = conn.await?;
let protocol = get_protocol(&connection)?;
View on GitHub (pinned to 074eb0b0d1)