{"record":{"id":"1d8c50c6a83e2745","repo":"nats-io/nats-server","slug":"incomplete-cert-chain-got-d-want-at-least-d","errorCode":null,"errorMessage":"incomplete cert chain, got %d, want at least %d","messagePattern":"incomplete cert chain, got (.+?), want at least (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/ocsp/ocsp.go","lineNumber":237,"sourceCode":"\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\nfunc SetOCSPStatus(t *testing.T, ocspURL, certPEM string, status int) {\n\tt.Helper()\n\n\tcert := parseCertPEM(t, certPEM)\n","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/internal/ocsp/ocsp.go#L219-L255","documentation":"GetOCSPStatus requires at least a leaf and its issuer in the first verified chain to check the OCSP response signature. If the chain has fewer than 2 certificates, validation cannot proceed and this error reports the actual length found.","triggerScenarios":"Calling GetOCSPStatus with a ConnectionState whose VerifiedChains[0] contains only the leaf cert (len < 2) — e.g. a self-signed or directly trusted leaf with no issuer appended.","commonSituations":"Client presenting a self-signed certificate trusted as a root; minimal test chains built with only one cert; RootCAs pool configured so only the leaf verifies without issuer in chain.","solutions":["Ensure the client sends (or the server's cert pool supplies) the issuer certificate so the chain has at least leaf + issuer","If using self-signed test certs, build a two-cert chain (root CA + leaf) instead of a single self-signed leaf","Guard with a length check on VerifiedChains[0] before calling GetOCSPStatus"],"exampleFix":"// before\nchain := [][]*x509.Certificate{{leaf}}\n// after\nchain := [][]*x509.Certificate{{leaf, caCert}}","handlingStrategy":"type-guard","validationCode":"cs := conn.ConnectionState()\nif len(cs.VerifiedChains) == 0 || len(cs.VerifiedChains[0]) < 2 {\n    return errors.New(\"chain too short for OCSP check (need leaf + issuer)\")\n}","typeGuard":"func hasLeafAndIssuer(cs tls.ConnectionState) bool {\n    return len(cs.VerifiedChains) > 0 && len(cs.VerifiedChains[0]) >= 2\n}","tryCatchPattern":"resp, err := GetOCSPStatus(cs)\nif err != nil {\n    if strings.HasPrefix(err.Error(), \"incomplete cert chain\") {\n        return nil, errOCSPNotPossible\n    }\n    return err\n}","preventionTips":["Configure clients to send the full chain (leaf + intermediates)","Include intermediates in the server's RootCAs/ClientCAs pool","Use test chains with at least two certs"],"tags":["tls","ocsp","certificate-chain"],"backgroundTag":"incomplete-certificate-chain","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}