txthinking/brook · error

failed to parse root certificate

Error message

failed to parse root certificate

What it means

NewBrookLink builds a TLS config for ws/wss-based brook links. When the link contains a 'ca' query parameter, its PEM content is parsed into an x509 CertPool; if AppendCertsFromPEM fails (no valid PEM certificates found), the link is rejected with this error. It signals the embedded CA material is malformed, truncated, or not PEM-encoded.

Source

Thrown at brooklink.go:99

		if kind == "wsserver" || kind == "wssserver" || kind == "quicserver" {
			if v.Get("address") != "" {
				address = v.Get("address")
			}
		}
		if kind == "wssserver" || kind == "quicserver" {
			h, _, err := net.SplitHostPort(u.Host)
			if err != nil {
				return nil, err
			}
			tc = &tls.Config{ServerName: h}
			if v.Get("insecure") == "true" {
				tc.InsecureSkipVerify = true
			}
			if v.Get("ca") != "" {
				roots := x509.NewCertPool()
				ok := roots.AppendCertsFromPEM([]byte(v.Get("ca")))
				if !ok {
					return nil, errors.New("failed to parse root certificate")
				}
				tc.RootCAs = roots
			}
			if kind == "wssserver" {
				tc.NextProtos = []string{"http/1.1"}
			}
			if kind == "quicserver" {
				tc.NextProtos = []string{"h3"}
			}
		}
		if kind == "wsserver" || kind == "wssserver" || kind == "quicserver" {
			if v.Get("withoutBrookProtocol") == "true" {
				p, err = SHA256Bytes([]byte(v.Get("password")))
				if err != nil {
					return nil, err
				}
			}
		}

View on GitHub (pinned to 5cd13ef3b1)

Solutions

  1. Re-encode the CA certificate as PEM (openssl x509 -inform DER -outform PEM) and percent-encode it fully when embedding in the link URL
  2. Verify the 'ca' parameter contains at least one complete '-----BEGIN CERTIFICATE-----' block with intact base64 lines
  3. If the CA is a system CA, drop the 'ca' parameter entirely instead of embedding it
  4. Test the PEM standalone: Go's AppendCertsFromPEM returns false only when no cert was added

Example fix

// before
blk := "wss://example.com/ws?ca=" + url.QueryEscape(strings.ReplaceAll(string(pemBytes), "\n", ""))
// after
blk := "wss://example.com/ws?ca=" + url.QueryEscape(string(pemBytes))
Defensive patterns

Strategy: validation

Validate before calling

func validCAPEM(ca string) bool {
	if ca == "" || !strings.Contains(ca, "-----BEGIN CERTIFICATE-----") {
		return false
	}
	pool := x509.NewCertPool()
	return pool.AppendCertsFromPEM([]byte(ca))
}
// before building the link: if !validCAPEM(caParam) { /* fix PEM/encoding */ }

Try / catch

if blk, err := NewBrookLink(link); err != nil {
	if strings.Contains(err.Error(), "failed to parse root certificate") {
		// re-encode CA to PEM and rebuild link
	}
	return err
}

Prevention

When it happens

Trigger: Creating a brook link (e.g. via NewRelayOverBrook) whose wss/wssserver URL has a 'ca' parameter containing data that x509.CertPool.AppendCertsFromPEM cannot parse as any PEM certificate block.

Common situations: CA cert pasted into the URL without proper PEM encoding or percent-encoding (newlines lost/mangled); truncated certificate; DER-encoded cert supplied instead of PEM; empty or whitespace/garbage value for 'ca'.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of txthinking/brook@5cd13ef3b1 (2026-09-06). Data as JSON: /api/errors/9bada43d7ede7dcd. Report an issue: GitHub.