cloudflare/quiche · error

initial send failed

Error message

initial send failed

What it means

connect_with_early_data calls conn.send() to emit the ClientHello and panics if the initial packet write fails. conn.send() returns Err when there is nothing to send or the connection is in a state that cannot emit packets (e.g. already closed/drained, or internal crypto failure building the initial packet).

Solutions

  1. Ensure the connection is freshly created and not closed before connect_with_early_data is called.
  2. Verify TLS/ALPN configuration on the quiche::Config is valid.
  3. Replace the expect with proper error handling and retry or reconnect logic.

Example fix

// before
let (write, send_info) = conn.send(&mut out).expect("initial send failed");
// after
let (write, send_info) = conn.send(&mut out)
    .map_err(|e| ClientError::Connect(format!("initial send failed: {e}")))?;
Defensive patterns

Strategy: try-catch

Validate before calling

if conn.is_closed() || conn.is_draining() {
    return Err(ClientError::Connect("connection already closed".into()));
}

Try / catch

match conn.send(&mut out) {
    Ok((w, info)) => (w, info),
    Err(e) => return Err(ClientError::Connect(format!("initial send failed: {e}"))),
}

Prevention

When it happens

Trigger: The connection is done/drained before the first send, TLS setup failed so no ClientHello can be built, or the output buffer is too small/misallocated.

Common situations: Reusing a closed quiche::Connection; invalid TLS configuration (bad ALPN/CA setup) during client creation; extremely small send buffer in custom code paths.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at h3i/src/client/sync_client.rs:219

    if let Some(session) = &args.session {
        conn.set_session(session)
            .map_err(|error| ClientError::Other(error.to_string()))?;
    }

    if let Some(keylog) = &mut keylog {
        if let Ok(keylog) = keylog.try_clone() {
            conn.set_keylog(Box::new(keylog));
        }
    }

    log::info!(
        "connecting to {peer_addr:} from {local_addr:} with scid {scid:?}",
    );

    let mut app_proto_selected = false;

    // Send ClientHello and initiate the handshake.
    let (write, send_info) = conn.send(&mut out).expect("initial send failed");

    let mut client = SyncClient::new(close_trigger_frames);
    // Send early data if connection is_in_early_data (resumption with 0-RTT was
    // successful) and if we have early_actions.
    if conn.is_in_early_data() {
        if let Some(early_actions) = early_actions {
            let mut early_action_iter = early_actions.iter();
            let mut wait_duration = None;
            let mut wait_instant = None;
            let mut waiting_for = WaitingFor::default();

            check_duration_and_do_actions(
                &mut wait_duration,
                &mut wait_instant,
                &mut early_action_iter,
                &mut conn,
                &mut waiting_for,
                client.stream_parsers_mut(),

View on GitHub (pinned to 9f96daa2c2)