ginuerzh/gost · error
connection queue is full
Error message
connection queue is full
What it means
The DNS-over-UDP server emulation in serve() creates a virtual connection and pushes it onto the listener's connChan; if the listener's backlog of unaccepted DNS 'connections' is full (non-blocking send), it rejects the new request with this error. It signals the server is accepting requests faster than the application's ServeDNS handler consumes them.
Source
Thrown at dns.go:229
}()
select {
case err := <-ln.errc:
return nil, err
default:
}
return ln, nil
}
func (l *dnsListener) serve(w dnsResponseWriter, mq []byte) (err error) {
conn := newDNSServerConn(l.addr, w.RemoteAddr())
conn.mq <- mq
select {
case l.connChan <- conn:
default:
return errors.New("connection queue is full")
}
select {
case mr := <-conn.mr:
_, err = w.Write(mr)
case <-conn.cclose:
err = io.EOF
}
return
}
func (l *dnsListener) ServeDNS(w dns.ResponseWriter, m *dns.Msg) {
b, err := m.Pack()
if err != nil {
log.Logf("[dns] %s: %v", l.addr, err)
return
}
if err := l.serve(w, b); err != nil {View on GitHub (pinned to a33fdbf4c9)
Solutions
- Ensure a concurrent Accept loop is running and processing connections promptly
- Increase accept queue capacity or spawn more handler goroutines
- Log and drop the request gracefully on this error (client will retry the query)
- Profile the handler behind Accept for blocking calls (network, locks)
Example fix
// before
conn.mq <- mq
select {
case l.connChan <- conn:
default:
return errors.New("connection queue is full")
}
// after
select {
case l.connChan <- conn:
default:
log.Logf("[dns] accept queue full, dropping query from %s", w.RemoteAddr())
return errors.New("connection queue is full") // caller drops; consider resizing queue
} Defensive patterns
Strategy: try-catch
Validate before calling
// cannot be pre-validated; concurrency/backpressure issue at accept time
if queueDepth(l.connChan) >= cap(l.connChan) {
return fmt.Errorf("dns accept queue near capacity: %d/%d", queueDepth(l.connChan), cap(l.connChan))
} Try / catch
if err := serve(w, r); err != nil {
if err.Error() == "connection queue is full" {
// drop query; DNS client will retry — optionally backpressure metric here
return
}
log.Log(err)
} Prevention
- Always run a dedicated Accept loop for the DNS listener before serving
- Keep the ServeDNS/ServeHTTP handler non-blocking; offload heavy work to goroutines
- Monitor queue depth and scale consumers under tunneling load spikes
- Consider resizing the connChan buffer if sustained overflow is observed
When it happens
Trigger: Calling ServeDNS/ServeHTTP for a DNS request when the DNSListener's connChan (capacity-limited accept queue) is already full — i.e., the caller of Accept() is not draining incoming DNS queries fast enough.
Common situations: A DNS tunnel server whose Accept loop is blocked or slow (downstream handler stalled), traffic spikes from DNS tunneling clients, or forgetting to start the Accept loop at all.
Related errors
- connection is closed
- broken pipe
- deadline not supported
- failed to perform an HTTPS request: %s
- empty chain
AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02).
Data as JSON: /api/errors/f0b6b9ae81d958f6.
Report an issue: GitHub.