cloudflare/quiche · error

error closing conn

Error message

error closing conn: {e:?}

What it means

When the HTTP/0.9 response handling loop finishes, the client closes the connection with conn.close(true, 0x00, b"kthxbye") and panics if close returns an error other than Done. Done and Ok are treated as success (Done means already closed). Any other quiche::Error (e.g. InvalidState) indicates the connection is in an unexpected state at close time.

Solutions

  1. Inspect the debug-formatted error in the message to identify the underlying quiche::Error.
  2. Check earlier logs for transport errors that may have left the connection drained.
  3. Treat this as a symptom: fix the root-cause connection failure first.
  4. Update quiche if a state that should map to Done is being reported as an error.
Defensive patterns

Strategy: validation

Validate before calling

// Check connection state before attempting graceful close in client code
if !conn.is_closed() && conn.is_drained() { /* skip close */ }

Prevention

When it happens

Trigger: Reaching the end of handle_responses while the underlying quiche::Connection is in a state where close() fails, e.g. after a fatal transport error already drained the connection.

Common situations: Connection already terminally errored or drained before the graceful close; race with peer-initiated close in lossy networks.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of cloudflare/quiche@9f96daa2c2 (2026-09-08). Data as JSON: /api/errors/25e7af16fdcdf416. Report an issue: GitHub.

Appendix: source

Thrown at apps/src/common.rs:542

                    debug!(
                        "{}/{} responses received",
                        self.reqs_complete, reqs_count
                    );

                    if self.reqs_complete == reqs_count {
                        info!(
                            "{}/{} response(s) received in {:?}, closing...",
                            self.reqs_complete,
                            reqs_count,
                            req_start.elapsed()
                        );

                        match conn.close(true, 0x00, b"kthxbye") {
                            // Already closed.
                            Ok(_) | Err(quiche::Error::Done) => (),

                            Err(e) => panic!("error closing conn: {e:?}"),
                        }

                        break;
                    }
                }
            }
        }
    }

    fn report_incomplete(&self, start: &std::time::Instant) -> bool {
        if self.reqs_complete != self.reqs.len() {
            error!(
                "connection timed out after {:?} and only completed {}/{} requests",
                start.elapsed(),
                self.reqs_complete,
                self.reqs.len()
            );

View on GitHub (pinned to 9f96daa2c2)