nats-io/nats-server · error
missing TLS verified chains
Error message
missing TLS verified chains
What it means
GetOCSPStatus staples/validates an OCSP response from the TLS connection state. It requires the handshake to have produced verified certificate chains; if tls.ConnectionState.VerifiedChains is empty (client cert verification didn't run or failed), this error is returned.
Source
Thrown at internal/ocsp/ocsp.go:232
}
func parsePEM(t *testing.T, pemPath string) *pem.Block {
t.Helper()
data, err := os.ReadFile(pemPath)
if err != nil {
t.Fatal(err)
}
block, _ := pem.Decode(data)
if block == nil {
t.Fatalf("failed to decode PEM %s", pemPath)
}
return block
}
func GetOCSPStatus(s tls.ConnectionState) (*ocsp.Response, error) {
if len(s.VerifiedChains) == 0 {
return nil, fmt.Errorf("missing TLS verified chains")
}
chain := s.VerifiedChains[0]
if got, want := len(chain), 2; got < want {
return nil, fmt.Errorf("incomplete cert chain, got %d, want at least %d", got, want)
}
leaf, issuer := chain[0], chain[1]
resp, err := ocsp.ParseResponseForCert(s.OCSPResponse, leaf, issuer)
if err != nil {
return nil, fmt.Errorf("failed to parse OCSP response: %w", err)
}
if err := resp.CheckSignatureFrom(issuer); err != nil {
return resp, err
}
return resp, nil
}
View on GitHub (pinned to 3a66a489d2)
Solutions
- Configure the TLS listener to request and verify client certs (tls.RequireAndVerifyClientCert or equivalent) so VerifiedChains is populated
- Guard the call: check len(connState.VerifiedChains) > 0 before invoking GetOCSPStatus
- If constructing ConnectionState manually (tests), populate VerifiedChains with the parsed chain
Example fix
// before
resp, err := GetOCSPStatus(tlsConn.ConnectionState())
// after
cs := tlsConn.ConnectionState()
if len(cs.VerifiedChains) == 0 {
return nil, errors.New("no verified chains: client cert verification did not run")
}
resp, err := GetOCSPStatus(cs) Defensive patterns
Strategy: type-guard
Validate before calling
cs := conn.ConnectionState()
if len(cs.VerifiedChains) == 0 {
return errors.New("no verified chains; ensure client cert verification is enabled")
} Type guard
func hasVerifiedChains(cs tls.ConnectionState) bool {
return len(cs.VerifiedChains) > 0
} Try / catch
resp, err := GetOCSPStatus(cs)
if err != nil {
if err.Error() == "missing TLS verified chains" {
return nil, errOCSPSkipped // treat as 'no OCSP data', not fatal
}
return err
} Prevention
- Set ClientAuth to verify client certs on the TLS listener
- Never build tls.ConnectionState by hand in production paths
- Add a startup assert that verification runs on test connections
When it happens
Trigger: Calling GetOCSPStatus with a ConnectionState where VerifiedChains is empty — e.g. the TLS session did not request/verify client certificates, verification was skipped (InsecureSkipVerify without manual chain building), or the state came from a session without peer certs.
Common situations: Server-side OCSP-stapling code run on connections where ClientAuth is NoClientCert; test harnesses constructing tls.ConnectionState manually without populating VerifiedChains; resumption paths where verification details are absent.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- failed to parse OCSP response: %w
- invalid chain link
- no available OCSP servers
- unable to plug TLS verify connection, config is nil
- OCSP peer verification for client connections requires TLS v
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/ad35c45c3f252cf3.
Report an issue: GitHub.