{"record":{"id":"b403748efee8d457","repo":"router-for-me/CLIProxyAPI","slug":"client-key-pem-is-invalid","errorCode":null,"errorMessage":"client key pem is invalid","messagePattern":"client key pem is invalid","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/home/certificate.go","lineNumber":264,"sourceCode":"\tif errWrite := os.WriteFile(path, raw, 0o600); errWrite != nil {\n\t\treturn errWrite\n\t}\n\treturn os.Chmod(path, 0o600)\n}\n\nfunc chmodCertificateFiles(paths certificatePaths) error {\n\tfor _, path := range []string{paths.ClientCert, paths.ClientKey, paths.CACert} {\n\t\tif errChmod := os.Chmod(path, 0o600); errChmod != nil {\n\t\t\treturn errChmod\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc parseRSAPrivateKeyPEM(raw []byte) (*rsa.PrivateKey, error) {\n\tblock, _ := pem.Decode(raw)\n\tif block == nil {\n\t\treturn nil, fmt.Errorf(\"client key pem is invalid\")\n\t}\n\tswitch block.Type {\n\tcase \"RSA PRIVATE KEY\":\n\t\treturn x509.ParsePKCS1PrivateKey(block.Bytes)\n\tcase \"PRIVATE KEY\":\n\t\tkey, errParse := x509.ParsePKCS8PrivateKey(block.Bytes)\n\t\tif errParse != nil {\n\t\t\treturn nil, errParse\n\t\t}\n\t\trsaKey, ok := key.(*rsa.PrivateKey)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"client key is not rsa\")\n\t\t}\n\t\treturn rsaKey, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"client key pem type %q is unsupported\", block.Type)\n\t}\n}","sourceCodeStart":246,"sourceCodeEnd":282,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/home/certificate.go#L246-L282","documentation":"Returned by parseRSAPrivateKeyPEM in internal/home/certificate.go when pem.Decode returns nil for the client key bytes — the file is not PEM at all. The enrollment/client-cert path requires an RSA private key in PEM form (PKCS#1 'RSA PRIVATE KEY' or PKCS#8 'PRIVATE KEY').","triggerScenarios":"Client key path points to a non-PEM file: a DER key, a JSON/seed file, an empty file, or a placeholder created by touch; key file truncated during provisioning.","commonSituations":"Provisioning script wrote the key in the wrong format; key downloaded via a channel that mangled it (HTML-escaped, base64-not-decoded); container secret mounted incorrectly leaving an empty file; file saved with a BOM.","solutions":["Check the first line is -----BEGIN ... PRIVATE KEY----- (RSA PRIVATE KEY or PRIVATE KEY)","If the key is DER, convert: openssl rsa -in key.der -inform DER -out key.pem","Re-run enrollment to regenerate the client key pair if the original is lost"],"exampleFix":"# before\nhome:\n  client-key: /etc/cliproxy/client.key.der\n\n# after\nopenssl rsa -in /etc/cliproxy/client.key.der -inform DER -out /etc/cliproxy/client.key\nhome:\n  client-key: /etc/cliproxy/client.key","handlingStrategy":"validation","validationCode":"if raw, err := os.ReadFile(cfg.ClientKey); err != nil {\n    return fmt.Errorf(\"client key unreadable: %w\", err)\n} else if _, ok := pem.Decode(raw); !ok {\n    return fmt.Errorf(\"client key %s is not PEM encoded\", cfg.ClientKey)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Checksum-verify provisioned key files during deployment","Never hand-edit or re-encode key files; copy bytes verbatim"],"tags":["go","tls","private-key","pem","home"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}