ginuerzh/gost · critical
quic: tlsconfig is nil
Error message
quic: tlsconfig is nil
What it means
tlsConfigQUICALPN clones the caller's *tls.Config and sets ALPN protocols (http/3, quic/v1) for QUIC transport; it panics immediately if the provided *tls.Config is nil. The library requires an explicit TLS configuration (certs, server name) before establishing a QUIC session or listener.
Source
Thrown at quic.go:342
}
gcm, err := cipher.NewGCM(c)
if err != nil {
return nil, err
}
nonceSize := gcm.NonceSize()
if len(data) < nonceSize {
return nil, errors.New("ciphertext too short")
}
nonce, ciphertext := data[:nonceSize], data[nonceSize:]
return gcm.Open(nil, nonce, ciphertext, nil)
}
func tlsConfigQUICALPN(tlsConfig *tls.Config) *tls.Config {
if tlsConfig == nil {
panic("quic: tlsconfig is nil")
}
tlsConfigQUIC := tlsConfig.Clone()
tlsConfigQUIC.NextProtos = []string{"http/3", "quic/v1"}
return tlsConfigQUIC
}
View on GitHub (pinned to a33fdbf4c9)
Solutions
- Provide a non-nil *tls.Config with ServerName and credentials (Certificates for server, RootCAs/InsecureSkipVerify only for tests) before dialing/listening over QUIC
- Check your config-construction code path for a missing tls assignment (zero-value struct passed through)
- Add an explicit nil check/early return in your own setup code with a clear error instead of reaching the library panic
- Load certificates from disk and verify they parse before handing the config to the library
Example fix
// before
cfg := &ClientConfig{} // TLS == nil -> panic: quic: tlsconfig is nil
// after
cfg := &ClientConfig{
TLSConfig: &tls.Config{ServerName: "example.com", RootCAs: pool},
} Defensive patterns
Strategy: type-guard
Validate before calling
func ensureTLSForQUIC(c *tls.Config) error {
if c == nil {
return errors.New("QUIC transport requires a non-nil *tls.Config (set ServerName and credentials)")
}
return nil
}
// call before dial/listen: if err := ensureTLSForQUIC(cfg.TLSConfig); err != nil { return err } Type guard
func hasTLSConfig(c *tls.Config) bool { return c != nil } Try / catch
// recover from the library panic as a last resort
func safeQUICListener(cfg *Config) (l net.Listener, err error) {
defer func() {
if r := recover(); r != nil {
if s, ok := r.(string); ok && strings.Contains(s, "tlsconfig is nil") {
err = errors.New("config.TLSConfig is nil — provide *tls.Config for QUIC")
return
}
panic(r)
}
}()
return QUICListener(cfg)
} Prevention
- Always populate the TLS field when constructing a config intended for QUIC transport
- When copying config structs, remember *tls.Config is a pointer — nil propagates silently
- Build tls.Config (ServerName + certs/RootCAs) right after loading application config, not at dial time
- Add your own nil check with a clear error before handing config to the library
- Since this is a panic, ensure top-level recover() middleware exists in server code
When it happens
Trigger: Calling QUIC dial/initSession or QUICListener with a config whose TLS field is nil — e.g. constructing the client Config without a *tls.Config, or reusing a zero-value config struct.
Common situations: Copying a config struct and dropping the TLS pointer; forgetting to build tls.Config{ServerName, Certificates/RootCAs} when switching from a TCP transport to QUIC; loading config from file where a tls section is absent.
Related errors
AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02).
Data as JSON: /api/errors/6d20c310b938bcf4.
Report an issue: GitHub.