{"record":{"id":"95c1ba29c826df15","repo":"EpicGames/lore","slug":"failed-to-setup-bidirectional-channel","errorCode":null,"errorMessage":"Failed to setup bidirectional channel","messagePattern":"Failed to setup bidirectional channel","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lore-server/src/quic/stream_handler.rs","lineNumber":908,"sourceCode":"        crypto_config.alpn_protocols = vec![protocol.as_bytes().into()];\n\n        let client_config = ClientConfig::new(Arc::new(\n            QuicClientConfig::try_from(crypto_config).expect(\"Failed client config\"),\n        ));\n\n        let client_addr: SocketAddr = \"0.0.0.0:0\".parse().unwrap();\n        let mut endpoint = Endpoint::client(client_addr).expect(\"Failed to create client endpoint\");\n        endpoint.set_default_client_config(client_config);\n\n        let connection = endpoint\n            .connect(server_addr, \"localhost\")\n            .unwrap()\n            .await\n            .unwrap();\n        let (send, recv) = connection\n            .open_bi()\n            .await\n            .expect(\"Failed to setup bidirectional channel\");\n\n        Harness {\n            send,\n            recv,\n            connection,\n            _server: server,\n            _endpoint: endpoint,\n        }\n    }\n\n    const MOCK_PROTOCOL: &str = \"mock-test/0.1\";\n    const MOCK_MAX_CHUNK: usize = 4096;\n\n    /// How long [`MockBehaviour::Block`] holds a permit: longer than any test runs, and unrelated\n    /// to the timeouts under test so changing one does not move the other.\n    const BLOCKING_HANDLER_SLEEP: Duration = Duration::from_secs(3600);\n\n    /// Time a refusal that involves no waiting is allowed to take, with room for a loaded host.","sourceCodeStart":890,"sourceCodeEnd":926,"githubUrl":"https://github.com/EpicGames/lore/blob/074eb0b0d1194c997d7cf28b55519e3e197b3e23/lore-server/src/quic/stream_handler.rs#L890-L926","documentation":"This expect wraps connection.open_bi() on the freshly established client connection in serve_and_connect. Failing here means the connection was not actually usable for streams — the handshake raced a server-side close, timed out, or was reset.","triggerScenarios":"Server dropped the connection immediately after accepting (factory/handler panic at accept), TLS handshake failed despite connect() resolving, idle/timeout transport error before streams could open, or server max concurrent streams is 0.","commonSituations":"ALPN negotiated but the StreamHandlerFactory rejects the protocol and closes the connection; server cert issues surfacing after connect; test racing server shutdown because the QuinnServer handle was dropped; transport config with zero bidirectional stream limits.","solutions":["Check connection.close_reason() / the open_bi ConnectionError for the real cause","Verify the StreamHandlerFactory accepts the given ALPN protocol and doesn't immediately close","Ensure the QuinnServer handle is kept alive for the whole test (Harness stores _server)","Confirm server transport config allows bidirectional streams (max_concurrent_bidi_streams > 0)"],"exampleFix":"// before\nlet (send, recv) = connection.open_bi().await.expect(\"Failed to setup bidirectional channel\");\n// after\nlet (send, recv) = connection.open_bi().await\n    .unwrap_or_else(|e| panic!(\"Failed to setup bidirectional channel: {e:?} (reason: {:?})\", connection.close_reason()));","handlingStrategy":"try-catch","validationCode":"// after connect(), before open_bi\nassert!(connection.close_reason().is_none(), \"connection died during handshake: {:?}\", connection.close_reason());\nassert_eq!(connection.handshake_data().map(|d| d.protocol).unwrap_or_default(), protocol.as_bytes());","typeGuard":"fn handshake_ok(conn: &quinn::Connection, alpn: &[u8]) -> bool {\n    conn.handshake_data().map(|d| d.protocol == alpn.to_vec()).unwrap_or(false)\n}","tryCatchPattern":"match connection.open_bi().await {\n    Ok(pair) => pair,\n    Err(e) => panic!(\"Failed to setup bidirectional channel: {e:?} (reason: {:?})\", connection.close_reason()),\n}","preventionTips":["Assert ALPN and handshake data before opening streams","Keep the QuinnServer handle alive for the entire test (Harness._server)","Ensure the StreamHandlerFactory accepts the test protocol rather than closing on accept","Verify server transport config permits bidirectional streams"],"tags":["quic","network","stream","rust","test-harness"],"backgroundTag":"connection-closed","analyzedSha":"074eb0b0d1194c997d7cf28b55519e3e197b3e23","analyzedAt":"2026-09-13T09:00:57.509Z","contentChangedAt":"2026-09-13T09:00:57.509Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}