EpicGames/lore · error
Failed to open additional stream
Error message
Failed to open additional stream
What it means
This is a test-helper panic in Harness::open_stream: quinn's connection.open_bi() returned an Err (or the await was cancelled by connection death), and the test harness unwraps it with this message. It means the QUIC connection could not allocate a new bidirectional stream — almost always because the connection has already been closed or the peer refused the stream.
Solutions
- Check the server-side handler/factory for panics or early connection.close() before open_bi is called
- Ensure the client connection is still alive: inspect connection.close_reason() before opening streams
- Verify ALPN protocol strings match between server QuinnConfigBuilder and client crypto_config.alpn_protocols
- Reduce open_stream calls or confirm MAX_STREAMS transport limits permit another bidirectional stream
Example fix
// before
self.connection.open_bi().await.expect("Failed to open additional stream")
// after
match self.connection.open_bi().await {
Ok(pair) => pair,
Err(e) => panic!("Failed to open additional stream: {:?} (close reason: {:?})", e, self.connection.close_reason()),
} Defensive patterns
Strategy: try-catch
Validate before calling
// before calling open_stream
if harness.connection.close_reason().is_some() {
panic!("connection already closed: {:?}", harness.connection.close_reason());
} Type guard
fn is_conn_alive(conn: &quinn::Connection) -> bool { conn.close_reason().is_none() } Try / catch
match self.connection.open_bi().await {
Ok((s, r)) => (s, r),
Err(quinn::ConnectionError::Closed(_) | quinn::ConnectionError::LocallyClosed) => panic!("connection closed before open_bi"),
Err(e) => panic!("Failed to open additional stream: {e:?}"),
} Prevention
- Check close_reason() before opening streams in tests
- Assert the server handler survived (no panic) before client-side stream operations
- Verify ALPN strings are identical constants shared by client and server
- Watch MAX_STREAMS limits in long-running loop tests
When it happens
Trigger: Calling open_bi() after the server dropped the connection, after an idle timeout, when the peer's MAX_STREAMS credit is exhausted, or after connection.close()/QuinnServer::start failed so no connection was ever established.
Common situations: Server handler panicked and killed the connection before the test opened a second stream; ALPN mismatch or certificate rejection closed the connection during handshake; idle timeout between test steps; stream-count limits hit after many open_stream calls.
Related errors
- Failed to setup bidirectional channel
- Failed to write header
- Failed flush
- Failed to read response
- Failed Quinn server start
AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13).
Data as JSON: /api/errors/2269a03d3ce6b361.
Report an issue: GitHub.
Appendix: source
Thrown at lore-server/src/quic/stream_handler.rs:841
///
/// Quinn's send and receive streams cannot be mocked, so exercising the stream handler needs
/// a real server. The server, endpoint and connection are held because dropping any of them
/// closes the stream.
struct Harness {
send: SendStream,
recv: RecvStream,
connection: quinn::Connection,
_server: QuinnServer,
_endpoint: Endpoint,
}
impl Harness {
/// Open an additional stream on the same connection.
async fn open_stream(&self) -> (SendStream, RecvStream) {
self.connection
.open_bi()
.await
.expect("Failed to open additional stream")
}
}
/// Send a request asking the handler for `behaviour`.
async fn request(send: &mut SendStream, behaviour: MockBehaviour, command_id: u32) {
send.write(&CommandHeader::new(behaviour.opcode(), command_id, 0).to_bytes())
.await
.expect("Failed to write header");
send.flush().await.expect("Failed flush");
}
/// Read one response header.
async fn response(recv: &mut RecvStream) -> CommandHeader {
let mut buffer = [0u8; 8];
recv.read_exact(&mut buffer)
.await
.expect("Failed to read response");
CommandHeader::from_bytes(&buffer)View on GitHub (pinned to 074eb0b0d1)