EpicGames/lore · error

Failed flush

Error message

Failed flush

What it means

This panic comes from request(): SendStream::flush returned Err. flush() waits for all buffered stream data to be acked-schedulable and fails under the same conditions as write — closed stream or dead connection.

Solutions

  1. Check for a close race: the handler finishing/closing right after consuming the header
  2. Verify the WriteError/CallDisconnect reason to distinguish ClosedStream from ConnectionLost
  3. Keep the server alive for the duration of the test (hold the QuinnServer handle, as Harness does with _server)
  4. If the race is inherent, downgrade flush to a best-effort match on ClosedStream

Example fix

// before
send.flush().await.expect("Failed flush");
// after
if let Err(e) = send.flush().await {
    panic!("Failed flush: {:?} (connection reason: {:?})", e, connection.close_reason());
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before flush, confirm connection is alive
if connection.close_reason().is_some() { panic!("connection closed before flush"); }

Type guard

fn conn_open(conn: &quinn::Connection) -> bool { conn.close_reason().is_none() }

Try / catch

if let Err(quinn::WriteError::ClosedStream) = send.flush().await {
    // benign race: handler closed the stream right after consuming the header
} else if let Err(e) = send.flush().await {
    panic!("Failed flush: {e:?}");
}

Prevention

When it happens

Trigger: flush() after the peer stopped the stream, after connection close, or when the connection was lost between write() and flush().

Common situations: Server closed the connection immediately after reading the header so the flush wait fails; handler panicked between header read and processing; QUIC connection idle timeout fired during the flush await.

Related errors


AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13). Data as JSON: /api/errors/091dff1d0b719ed4. Report an issue: GitHub.

Appendix: source

Thrown at lore-server/src/quic/stream_handler.rs:850

        _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)
    }

    /// Serve `factory` for `protocol` on a loopback endpoint and open a client stream to it.
    ///
    /// The client skips certificate verification and presents none of its own; the mTLS tests
    /// build their endpoints by hand because varying exactly that is what they test.
    async fn serve_and_connect(
        factory: Box<dyn StreamHandlerFactory>,
        protocol: &'static str,

View on GitHub (pinned to 074eb0b0d1)