{"record":{"id":"9bada43d7ede7dcd","repo":"txthinking/brook","slug":"failed-to-parse-root-certificate","errorCode":null,"errorMessage":"failed to parse root certificate","messagePattern":"failed to parse root certificate","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"brooklink.go","lineNumber":99,"sourceCode":"\t\tif kind == \"wsserver\" || kind == \"wssserver\" || kind == \"quicserver\" {\n\t\t\tif v.Get(\"address\") != \"\" {\n\t\t\t\taddress = v.Get(\"address\")\n\t\t\t}\n\t\t}\n\t\tif kind == \"wssserver\" || kind == \"quicserver\" {\n\t\t\th, _, err := net.SplitHostPort(u.Host)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\ttc = &tls.Config{ServerName: h}\n\t\t\tif v.Get(\"insecure\") == \"true\" {\n\t\t\t\ttc.InsecureSkipVerify = true\n\t\t\t}\n\t\t\tif v.Get(\"ca\") != \"\" {\n\t\t\t\troots := x509.NewCertPool()\n\t\t\t\tok := roots.AppendCertsFromPEM([]byte(v.Get(\"ca\")))\n\t\t\t\tif !ok {\n\t\t\t\t\treturn nil, errors.New(\"failed to parse root certificate\")\n\t\t\t\t}\n\t\t\t\ttc.RootCAs = roots\n\t\t\t}\n\t\t\tif kind == \"wssserver\" {\n\t\t\t\ttc.NextProtos = []string{\"http/1.1\"}\n\t\t\t}\n\t\t\tif kind == \"quicserver\" {\n\t\t\t\ttc.NextProtos = []string{\"h3\"}\n\t\t\t}\n\t\t}\n\t\tif kind == \"wsserver\" || kind == \"wssserver\" || kind == \"quicserver\" {\n\t\t\tif v.Get(\"withoutBrookProtocol\") == \"true\" {\n\t\t\t\tp, err = SHA256Bytes([]byte(v.Get(\"password\")))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t}","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/txthinking/brook/blob/5cd13ef3b1fb574e88ebf2c1b5d95f2ebe1342c8/brooklink.go#L81-L117","documentation":"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.","triggerScenarios":"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.","commonSituations":"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'.","solutions":["Re-encode the CA certificate as PEM (openssl x509 -inform DER -outform PEM) and percent-encode it fully when embedding in the link URL","Verify the 'ca' parameter contains at least one complete '-----BEGIN CERTIFICATE-----' block with intact base64 lines","If the CA is a system CA, drop the 'ca' parameter entirely instead of embedding it","Test the PEM standalone: Go's AppendCertsFromPEM returns false only when no cert was added"],"exampleFix":"// before\nblk := \"wss://example.com/ws?ca=\" + url.QueryEscape(strings.ReplaceAll(string(pemBytes), \"\\n\", \"\"))\n// after\nblk := \"wss://example.com/ws?ca=\" + url.QueryEscape(string(pemBytes))","handlingStrategy":"validation","validationCode":"func validCAPEM(ca string) bool {\n\tif ca == \"\" || !strings.Contains(ca, \"-----BEGIN CERTIFICATE-----\") {\n\t\treturn false\n\t}\n\tpool := x509.NewCertPool()\n\treturn pool.AppendCertsFromPEM([]byte(ca))\n}\n// before building the link: if !validCAPEM(caParam) { /* fix PEM/encoding */ }","typeGuard":null,"tryCatchPattern":"if blk, err := NewBrookLink(link); err != nil {\n\tif strings.Contains(err.Error(), \"failed to parse root certificate\") {\n\t\t// re-encode CA to PEM and rebuild link\n\t}\n\treturn err\n}","preventionTips":["Always PEM-encode CA certs and percent-encode the full string in the URL","Verify PEM round-trips with AppendCertsFromPEM before embedding","Prefer dropping 'ca' for public/system CAs"],"tags":["tls","certificate-parsing","configuration"],"backgroundTag":"invalid-config-value","analyzedSha":"5cd13ef3b1fb574e88ebf2c1b5d95f2ebe1342c8","analyzedAt":"2026-09-06T04:35:00.432Z","contentChangedAt":"2026-09-06T04:35:00.432Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}