gravitational/teleport · error
override certificate should not be included in chain
Error message
override certificate should not be included in chain
What it means
validateCertificateOverride checks that the override certificate is not duplicated inside its own chain. The chain must contain only issuers above the override certificate; if chain[0] has the same Subject as the override certificate itself, the config is redundant/ambiguous and this error is thrown with the field hint 'chain[0]'.
Source
Thrown at lib/subca/parsed.go:290
const maxChainLength = 10
if len(co.GetChain()) > maxChainLength {
return nil, "chain", fmt.Errorf(
"certificate chain has too many entries (%d > %d)", len(co.GetChain()), maxChainLength)
}
chain = make([]*x509.Certificate, len(co.GetChain()))
prev := cert
for i, chainPEM := range co.GetChain() {
chainCert, err := ParseCertificateOverrideCertificate(chainPEM)
if err != nil {
return nil, fmt.Sprintf("chain[%d]", i), err
}
chainSub := chainCert.Subject.String()
// Certificate not in chain.
if i == 0 && cert.Subject.String() == chainSub {
return nil, fmt.Sprintf("chain[%d]", i),
errors.New("override certificate should not be included in chain")
}
// Issuer/Subject relationship.
if issuer := prev.Issuer.String(); issuer != chainSub {
return nil, fmt.Sprintf("chain[%d]", i),
fmt.Errorf("chain out of order, subject=%q (want %q)", chainSub, issuer)
}
// Verify signature.
if err := prev.CheckSignatureFrom(chainCert); err != nil {
return nil,
fmt.Sprintf("chain[%d]", i),
fmt.Errorf("chain signature check failed, previous certificate not signed by current: %w", err)
}
// Note: we purposefully avoid time-based chain validation at this layer,
// as that could make an override that was once valid impossible to
// bootstrap or update without destructive action.View on GitHub (pinned to 1283425b60)
Solutions
- Remove the duplicate certificate from the beginning of the chain; the chain should start with the direct issuer of the override certificate.
- If the certificate is the root of what you have, drop the chain entirely and supply only the override certificate.
- Rebuild the chain bottom-up: chain[0] must issue the override cert, each following entry must issue the previous one.
Example fix
// before chain[0] = override cert (same Subject) -> rejected // after chain[0] = intermediate that signed the override cert chain[1] = root (optional)
Defensive patterns
Strategy: validation
Validate before calling
certSub := parseSubject(override.Cert)
if len(override.Chain) > 0 && parseSubject(override.Chain[0]) == certSub {
return fmt.Errorf("override certificate duplicated in chain[0]")
} Prevention
- Compare Subject strings of cert and chain[0] before submitting the override.
- Generate chains programmatically with a bottom-up issuer walk instead of hand-pasting PEMs.
- Keep the override certificate in exactly one field.
When it happens
Trigger: Calling ValidateAndParseCAOverride where co.Chain[0]'s Subject string equals the Subject of the parsed override certificate, i.e. the same cert was pasted into both fields.
Common situations: Copying an entire PEM file (override cert + intermediates) into both 'cert' and 'chain'; scripts that split PEM bundles off-by-one so the CA lands in both fields; hand-built chains that start with the CA itself instead of its issuer.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- chain not allowed with an empty certificate
- not a CA certificate (IsCA=false)
- basic constraints not valid (BasicConstraintsValid=false)
- missing KeyUsage keyCertSign
- missing KeyUsage cRLSign
AI-assisted analysis of gravitational/teleport@1283425b60 (2026-09-02).
Data as JSON: /api/errors/88636d17bd36ad4b.
Report an issue: GitHub.