{"record":{"id":"ad35c45c3f252cf3","repo":"nats-io/nats-server","slug":"missing-tls-verified-chains","errorCode":null,"errorMessage":"missing TLS verified chains","messagePattern":"missing TLS verified chains","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/ocsp/ocsp.go","lineNumber":232,"sourceCode":"}\n\nfunc parsePEM(t *testing.T, pemPath string) *pem.Block {\n\tt.Helper()\n\tdata, err := os.ReadFile(pemPath)\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\tblock, _ := pem.Decode(data)\n\tif block == nil {\n\t\tt.Fatalf(\"failed to decode PEM %s\", pemPath)\n\t}\n\treturn block\n}\n\nfunc GetOCSPStatus(s tls.ConnectionState) (*ocsp.Response, error) {\n\tif len(s.VerifiedChains) == 0 {\n\t\treturn nil, fmt.Errorf(\"missing TLS verified chains\")\n\t}\n\tchain := s.VerifiedChains[0]\n\n\tif got, want := len(chain), 2; got < want {\n\t\treturn nil, fmt.Errorf(\"incomplete cert chain, got %d, want at least %d\", got, want)\n\t}\n\tleaf, issuer := chain[0], chain[1]\n\n\tresp, err := ocsp.ParseResponseForCert(s.OCSPResponse, leaf, issuer)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse OCSP response: %w\", err)\n\t}\n\tif err := resp.CheckSignatureFrom(issuer); err != nil {\n\t\treturn resp, err\n\t}\n\treturn resp, nil\n}\n","sourceCodeStart":214,"sourceCodeEnd":250,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/internal/ocsp/ocsp.go#L214-L250","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nresp, err := GetOCSPStatus(tlsConn.ConnectionState())\n// after\ncs := tlsConn.ConnectionState()\nif len(cs.VerifiedChains) == 0 {\n    return nil, errors.New(\"no verified chains: client cert verification did not run\")\n}\nresp, err := GetOCSPStatus(cs)","handlingStrategy":"type-guard","validationCode":"cs := conn.ConnectionState()\nif len(cs.VerifiedChains) == 0 {\n    return errors.New(\"no verified chains; ensure client cert verification is enabled\")\n}","typeGuard":"func hasVerifiedChains(cs tls.ConnectionState) bool {\n    return len(cs.VerifiedChains) > 0\n}","tryCatchPattern":"resp, err := GetOCSPStatus(cs)\nif err != nil {\n    if err.Error() == \"missing TLS verified chains\" {\n        return nil, errOCSPSkipped // treat as 'no OCSP data', not fatal\n    }\n    return err\n}","preventionTips":["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"],"tags":["tls","ocsp","certificate"],"backgroundTag":"missing-tls-verified-chains","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}