EpicGames/lore · error

Failed to read response

Error message

Failed to read response

What it means

This panic comes from response(): RecvStream::read_exact failed. read_exact returns Finished when the peer ended the stream before producing 8 header bytes, or an error when the connection is lost/reset — either way the full CommandHeader could not be read.

Solutions

  1. Confirm the server handler actually writes a full 8-byte CommandHeader for the given opcode
  2. Check server-side panics/logs for the MockBehaviour under test
  3. Verify CommandHeader serialization size still equals the 8-byte buffer the harness reads
  4. Distinguish Finished (early EOF, handler bug) from ConnectionLost (transport bug) in the ReadExactError

Example fix

// before
recv.read_exact(&mut buffer).await.expect("Failed to read response");
// after
recv.read_exact(&mut buffer).await.unwrap_or_else(|e| panic!("Failed to read response: {:?}", e));
// and ensure the handler writes: send.write_all(&CommandHeader::new(...).to_bytes()).await?
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the handler writes a response; on the server side:
// send.write_all(&CommandHeader::new(...).to_bytes()).await?;

Type guard

fn got_full_header(n: usize) -> bool { n == 8 }

Try / catch

match recv.read_exact(&mut buffer).await {
    Ok(()) => CommandHeader::from_bytes(&buffer),
    Err(quinn::ReadExactError::FinishedEarly) => panic!("server ended stream before sending a full response header"),
    Err(quinn::ReadExactError::ReadError(e)) => panic!("Failed to read response: {e:?}"),
}

Prevention

When it happens

Trigger: Server wrote fewer than 8 bytes (short/empty CommandHeader) then closed the stream; server never responded because the handler ignored the command; connection dropped mid-response.

Common situations: Handler implementation under test writes no response for the given opcode; handler panicked before writing a reply; response header serialization size changed so read_exact's fixed 8-byte buffer no longer matches.

Related errors


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

Appendix: source

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

                .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,
    ) -> Harness {
        let socket = UdpSocket::bind("127.0.0.1:0").unwrap();
        let server_addr = socket.local_addr().expect("Failed socket setup");
        drop(socket);

        let (cert_path, key_path, _) = server_certs().expect("Bad cert paths");
        let server = QuinnServer::start(
            QuinnConfigBuilder::new()

View on GitHub (pinned to 074eb0b0d1)