txthinking/brook · error

quic max datagram size is 1197

Error message

quic max datagram size is 1197

What it means

QUICClient.UDPHandle enforces a maximum QUIC datagram payload size of 1197 bytes. The check 12+4+1+len(DstAddr)+2+len(Data)+16 counts the QUIC packet header, the SOCKS address block (ATyp + address + port), the datagram payload, and the AEAD overhead (16 bytes). If the client's datagram (address + payload) would exceed this QUIC datagram ceiling, the relay is rejected before any packet is sent.

Source

Thrown at quicclient.go:146

		}
		if err := sc.Exchange(c); err != nil {
			return nil
		}
		return nil
	}
	if r.Cmd == socks5.CmdUDP {
		_, err := r.UDP(c, x.Server.ServerAddr)
		if err != nil {
			return err
		}
		return nil
	}
	return socks5.ErrUnsupportCmd
}

func (x *QUICClient) UDPHandle(s *socks5.Server, addr *net.UDPAddr, d *socks5.Datagram) error {
	if 12+4+1+len(d.DstAddr)+2+len(d.Data)+16 > 1197 {
		return errors.New("quic max datagram size is 1197")
	}
	dstb := append(append([]byte{d.Atyp}, d.DstAddr...), d.DstPort...)
	conn, err := x.PacketConnFactory.Handle(addr, dstb, d.Data, func(b []byte) (int, error) {
		d.Data = b
		return s.UDPConn.WriteToUDP(d.Bytes(), addr)
	}, x.UDPTimeout)
	if err != nil {
		return err
	}
	if conn == nil {
		return nil
	}
	defer conn.Close()
	sa := x.ServerAddress
	if sa == "" {
		sa = x.ServerHost
	}
	rc, err := QUICDialUDP(addr.String(), d.Address(), sa, x.TLSConfig, x.UDPTimeout)

View on GitHub (pinned to 5cd13ef3b1)

Solutions

  1. Reduce the outgoing UDP datagram payload size to fit: keep len(DstAddr) + len(Data) below 1197 - 34 bytes (about 1163 bytes minus address bytes).
  2. For DNS traffic, prefer TCP or DNS-over-HTTPS for responses exceeding the limit, or enable EDNS truncation so resolvers send TC=1 instead of oversized UDP answers.
  3. If the tunnel must carry large datagrams, switch the transport to a stream-based protocol (e.g. the TCP/TLS client) instead of QUIC datagrams.

Example fix

// before
d.Data = bigPayload // e.g. 1300 bytes
client.UDPHandle(server, addr, d) // panics back with "quic max datagram size is 1197"

// after
const maxQuicDatagram = 1197 - 34 - len(d.DstAddr) // overhead + address
if len(d.Data) > maxQuicDatagram {
    d.Data = d.Data[:maxQuicDatagram] // or fragment / use TCP transport
}
Defensive patterns

Strategy: validation

Validate before calling

const maxQuicPayload = 1197 - 12 - 4 - 1 - 2 - 16 // 1162 minus address bytes
func fitsQuicDatagram(dstAddr, data []byte) bool {
    return 12+4+1+len(dstAddr)+2+len(data)+16 <= 1197
}

Try / catch

// Go: inspect the error from UDPHandle
if err := client.UDPHandle(srv, addr, d); err != nil && strings.Contains(err.Error(), "max datagram size") {
    // fall back to TCP transport or fragment the payload
}

Prevention

When it happens

Trigger: Calling QUICClient.UDPHandle (via the socks5 server UDP path) with a Datagram whose DstAddr plus Data length exceeds 1197 - 34 bytes of fixed overhead, i.e. a payload over roughly 1163 bytes minus address length (long IPv6/hostname targets shrink the usable payload further).

Common situations: Clients sending large UDP payloads through the QUIC transport, e.g. DNS responses with large answers, a UDP datagram close to the 1500-byte Ethernet MTU, or apps ignoring QUIC's smaller effective MTU compared to plain UDP forwarding.

Understand the failure class

Background: payload too large / request exceeds maximum size: why libraries cap bytes and how to fix oversize payloads — this error's family across 50 libraries.

Related errors


AI-assisted analysis of txthinking/brook@5cd13ef3b1 (2026-09-06). Data as JSON: /api/errors/1790d4ed7b41afe8. Report an issue: GitHub.