cloudflare/cloudflared · error

Failed to send datagram back to edge

Error message

Failed to send datagram back to edge

What it means

In quic/datagramv2.go SendToSession, once the payload carries the session ID and type byte, dm.session.SendDatagram(msgWithIDAndType) transmits it to Cloudflare's edge. An error here means the QUIC connection could not accept the datagram, and the UDP packet is dropped.

Source

Thrown at quic/datagramv2.go:89

}

// SendToSession suffix the session ID and datagram version to the payload so the other end of the QUIC connection can
// demultiplex the payload from multiple datagram sessions
func (dm *DatagramMuxerV2) SendToSession(session *packet.Session) error {
	if len(session.Payload) > dm.mtu() {
		packetTooBigDropped.Inc()
		return fmt.Errorf("origin UDP payload has %d bytes, which exceeds transport MTU %d", len(session.Payload), dm.mtu())
	}
	msgWithID, err := SuffixSessionID(session.ID, session.Payload)
	if err != nil {
		return errors.Wrap(err, "Failed to suffix session ID to datagram, it will be dropped")
	}
	msgWithIDAndType, err := SuffixType(msgWithID, DatagramTypeUDP)
	if err != nil {
		return errors.Wrap(err, "Failed to suffix datagram type, it will be dropped")
	}
	if err := dm.session.SendDatagram(msgWithIDAndType); err != nil {
		return errors.Wrap(err, "Failed to send datagram back to edge")
	}
	return nil
}

// SendPacket sends a packet with datagram version in the suffix. If ctx is a TracedContext, it adds the tracing
// context between payload and datagram version.
// The other end of the QUIC connection can demultiplex by parsing the payload as IP and look at the source and destination.
func (dm *DatagramMuxerV2) SendPacket(pk Packet) error {
	payloadWithMetadata, err := suffixMetadata(pk.Payload(), pk.Metadata())
	if err != nil {
		return err
	}
	payloadWithMetadataAndType, err := SuffixType(payloadWithMetadata, pk.Type())
	if err != nil {
		return errors.Wrap(err, "Failed to suffix datagram type, it will be dropped")
	}
	if err := dm.session.SendDatagram(payloadWithMetadataAndType); err != nil {
		return errors.Wrap(err, "Failed to send datagram back to edge")

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Check tunnel health/logs for QUIC reconnects; transient errors resolve after reconnection.
  2. Retry at the application layer — UDP over the tunnel tolerates loss.
  3. Throttle UDP traffic if the send queue is overflowing.
  4. Switch protocol (--protocol http2) or upgrade cloudflared if QUIC datagram failures are persistent on your network path.
Defensive patterns

Strategy: retry

Validate before calling

select {
case <-muxer.Ready(): // connection available
    return muxer.SendToSession(datagram)
default:
    return errors.New("datagram muxer not connected to edge")
}

Try / catch

err := muxer.SendToSession(datagram)
if err != nil && strings.Contains(err.Error(), "send datagram back to edge") {
    // bounded retry with backoff during edge reconnects
    return retryWithBackoff(3, 100*time.Millisecond, func() error {
        return muxer.SendToSession(datagram)
    })
}

Prevention

When it happens

Trigger: dm.session.SendDatagram returns an error during a v2 datagram send — QUIC connection closed/reconnecting, or the datagram send queue is full.

Common situations: Edge connection drop or handshake in progress; sustained UDP traffic exceeding datagram throughput (queue backpressure); cloudflared shutdown while UDP sessions persist.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/2b70205aec9cb4bd. Report an issue: GitHub.