bytebase/bytebase · error
start TLS: %v
Error message
start TLS: %v
What it means
In START_TLS mode, after the plain connection succeeds, dial upgrades it with conn.StartTLS(tlsConfig). If the TLS handshake fails, the connection is closed and the error is wrapped as 'start TLS: %v'. Common root causes are certificate trust and protocol mismatch issues on the port serving StartTLS.
Source
Thrown at backend/plugin/idp/ldap/ldap.go:108
InsecureSkipVerify: p.config.SkipTLSVerify,
}
switch p.config.SecurityProtocol {
case storepb.LDAPIdentityProviderConfig_LDAPS:
url := fmt.Sprintf("ldaps://%s:%d", p.config.Host, p.config.Port)
conn, err := ldap.DialURL(url, ldap.DialWithTLSConfig(tlsConfig))
if err != nil {
return nil, errors.Errorf("dial TLS: %v", err)
}
return conn, nil
case storepb.LDAPIdentityProviderConfig_START_TLS:
url := fmt.Sprintf("ldap://%s:%d", p.config.Host, p.config.Port)
conn, err := ldap.DialURL(url)
if err != nil {
return nil, errors.Errorf("dial: %v", err)
}
if err := conn.StartTLS(tlsConfig); err != nil {
_ = conn.Close()
return nil, errors.Errorf("start TLS: %v", err)
}
return conn, nil
default:
url := fmt.Sprintf("ldap://%s:%d", p.config.Host, p.config.Port)
conn, err := ldap.DialURL(url)
if err != nil {
return nil, errors.Errorf("dial: %v", err)
}
return conn, nil
}
}
// Connect establishes a connection using the bind DN and bind password.
func (p *IdentityProvider) Connect() (*ldap.Conn, error) {
conn, err := p.dial()
if err != nil {
return nil, err
}View on GitHub (pinned to 1870550677)
Solutions
- Load the directory server's CA certificate into the TLS config's RootCAs.
- Set ServerName in the TLS config (or fix Host) so it matches the certificate.
- Check the certificate expiry and renew if needed.
- Align MinVersion/MaxVersion and cipher suites on the TLS config with the server's requirements.
- Debug with openssl s_client -starttls ldap -connect host:389 to see the handshake failure.
Example fix
// before
tlsConfig := &tls.Config{} // no RootCAs, self-signed server cert
// after
tlsConfig := &tls.Config{RootCAs: caPool, ServerName: "ldap.example.com"} Defensive patterns
Strategy: try-catch
Validate before calling
func startTLSHandshakeOK(host string, port int, caPool *x509.CertPool) error {
conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), 5*time.Second)
if err != nil { return err }
defer conn.Close()
tc := tls.Client(conn, &tls.Config{RootCAs: caPool, ServerName: host})
return tc.Handshake()
}
// pre-flight before Connect Try / catch
conn, err := p.dial()
if err != nil {
if strings.Contains(err.Error(), "start TLS") {
return nil, fmt.Errorf("StartTLS handshake failed for %s:%d (check CA cert/hostname): %w", p.config.Host, p.config.Port, err)
}
return nil, err
} Prevention
- Load the server CA into RootCAs; set ServerName to match the certificate.
- Monitor certificate expiry.
- Pin TLS versions/ciphers consistent with the server.
- Test with openssl s_client -starttls ldap -connect host:389.
When it happens
Trigger: Calling Connect with SecurityProtocol START_TLS where the server's certificate is untrusted (self-signed or internal CA not in tlsConfig.RootCAs), the certificate hostname does not match config.Host, or the negotiated TLS version/ciphers are unsupported.
Common situations: Corporate LDAP servers with internal CA certificates not distributed to clients; expired server certificates; servers requiring TLS 1.2+ while the client offers older versions; pointing StartTLS at an LDAPS-only port.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- no valid CERTIFICATE PEM block found
- no CERTIFICATE PEM block found
- invalid ssl_ca PEM
- invalid ssl_cert PEM
- failed to parse CA certificate
AI-assisted analysis of bytebase/bytebase@1870550677 (2026-09-06).
Data as JSON: /api/errors/dc6a8dcfe76da4b5.
Report an issue: GitHub.