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

  1. Provide a non-nil *tls.Config with ServerName and credentials (Certificates for server, RootCAs/InsecureSkipVerify only for tests) before dialing/listening over QUIC
  2. Check your config-construction code path for a missing tls assignment (zero-value struct passed through)
  3. Add an explicit nil check/early return in your own setup code with a clear error instead of reaching the library panic
  4. 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

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.