snail007/goproxy · error
failed to parse root certificate
Error message
failed to parse root certificate
What it means
getRequestTlsConfig builds a tls.Config for an outgoing TLS connection to the proxy parent. After loading PEM bytes it calls x509 CertPool.AppendCertsFromPEM, and this error is thrown when the returned ok flag is false, i.e. none of the supplied bytes could be parsed as a PEM-encoded certificate. The library requires a valid root/CA certificate file to authenticate the proxy, so it aborts instead of silently skipping verification (InsecureSkipVerify is false).
Source
Thrown at utils/functions.go:134
if err != nil {
return
}
_conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", host, port), time.Duration(timeout)*time.Millisecond)
if err != nil {
return
}
return *tls.Client(_conn, conf), err
}
func getRequestTlsConfig(certBytes, keyBytes []byte) (conf *tls.Config, err error) {
var cert tls.Certificate
cert, err = tls.X509KeyPair(certBytes, keyBytes)
if err != nil {
return
}
serverCertPool := x509.NewCertPool()
ok := serverCertPool.AppendCertsFromPEM(certBytes)
if !ok {
err = errors.New("failed to parse root certificate")
}
conf = &tls.Config{
RootCAs: serverCertPool,
Certificates: []tls.Certificate{cert},
ServerName: "proxy",
InsecureSkipVerify: false,
}
return
}
func ConnectHost(hostAndPort string, timeout int) (conn net.Conn, err error) {
conn, err = net.DialTimeout("tcp", hostAndPort, time.Duration(timeout)*time.Millisecond)
return
}
func ListenTls(ip string, port int, certBytes, keyBytes []byte) (ln *net.Listener, err error) {
var cert tls.Certificate
cert, err = tls.X509KeyPair(certBytes, keyBytes)
if err != nil {View on GitHub (pinned to e6d6a821db)
Solutions
- Regenerate or re-export the CA certificate in PEM format (openssl x509 -in cert.der -out ca.pem -outform PEM) and point the config at that file
- Verify the file actually contains PEM blocks: head the file and confirm '-----BEGIN CERTIFICATE-----' lines exist and it is not empty or a key
- Check the config path for typos and that the process has read permission on the file
- As a last resort for testing only, run with verification skipped (insecure mode), but never in production
Example fix
// before (certBytes from a DER file -> AppendCertsFromPEM returns false) ok := serverCertPool.AppendCertsFromPEM(certBytes) // after: ensure the file is PEM on disk // openssl x509 -in ca.crt -out ca.pem -outform PEM // then start with ca.pem configured ok := serverCertPool.AppendCertsFromPEM(certBytes) // ok == true
Defensive patterns
Strategy: validation
Validate before calling
func validPEM(path string) error {
b, err := os.ReadFile(path)
if err != nil { return err }
if !bytes.Contains(b, []byte("-----BEGIN CERTIFICATE-----")) {
return fmt.Errorf("%s is not a PEM certificate", path)
}
return nil
}
// call before starting the service: validPEM(cfg.CertPath) Try / catch
if err := startService(cfg); err != nil {
if strings.Contains(err.Error(), "failed to parse root certificate") {
// surface config guidance: check CA file is PEM
}
} Prevention
- Always distribute CA certs in PEM format (BEGIN CERTIFICATE blocks)
- Validate cert files at deploy time (openssl x509 -in ca.pem -noout) before launch
- Use absolute, permission-checked paths for cert config
- Regenerate certs with a single scripted pipeline to avoid format drift
When it happens
Trigger: The certificate file configured for the TLS parent points to a missing, empty, non-PEM, or corrupt file; AppendCertsFromPEM returns false when certBytes contains no parseable PEM CERTIFICATE blocks, so getRequestTlsConfig constructs the error and TlsConnect fails.
Common situations: Config pointing root CA path at the wrong file or a private key instead of a certificate; certificate generated in DER format instead of PEM; file truncated by a failed copy/mount; using an expired or malformed cert generated by a broken tooling pipeline.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
AI-assisted analysis of snail007/goproxy@e6d6a821db (2026-09-03).
Data as JSON: /api/errors/411f6ad9f4d6bb2d.
Report an issue: GitHub.