{"record":{"id":"c1dabf2598bc08a4","repo":"router-for-me/CLIProxyAPI","slug":"home-certificate-request-failed","errorCode":null,"errorMessage":"home certificate request failed","messagePattern":"home certificate request failed","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/home/certificate.go","lineNumber":330,"sourceCode":"\t}\n\tdefer func() {\n\t\t_ = conn.Close()\n\t}()\n\tif deadline, ok := dialCtx.Deadline(); ok {\n\t\t_ = conn.SetDeadline(deadline)\n\t}\n\tif _, errWrite := conn.Write(encodeRESPArray(\"CERTIFICATE\", \"REQUEST\", claims.CertificateID, claims.EnrollmentSecret, string(csrPEM))); errWrite != nil {\n\t\treturn response, errWrite\n\t}\n\traw, errRead := readRESPBulk(bufio.NewReader(conn))\n\tif errRead != nil {\n\t\treturn response, errRead\n\t}\n\tif errUnmarshal := json.Unmarshal(raw, &response); errUnmarshal != nil {\n\t\treturn response, errUnmarshal\n\t}\n\tif !response.OK {\n\t\treturn response, fmt.Errorf(\"home certificate request failed\")\n\t}\n\treturn response, nil\n}\n\nfunc encodeRESPArray(args ...string) []byte {\n\tvar buf bytes.Buffer\n\tbuf.WriteString(\"*\")\n\tbuf.WriteString(strconv.Itoa(len(args)))\n\tbuf.WriteString(\"\\r\\n\")\n\tfor _, arg := range args {\n\t\tbuf.WriteString(\"$\")\n\t\tbuf.WriteString(strconv.Itoa(len(arg)))\n\t\tbuf.WriteString(\"\\r\\n\")\n\t\tbuf.WriteString(arg)\n\t\tbuf.WriteString(\"\\r\\n\")\n\t}\n\treturn buf.Bytes()\n}","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/home/certificate.go#L312-L348","documentation":"Returned by the certificate-request exchange in internal/home/certificate.go: the client sent 'CERTIFICATE REQUEST <id> <secret> <csr>' over the RESP connection and received a JSON response with OK=false. The home server explicitly rejected the enrollment — the transport worked but the request was denied.","triggerScenarios":"Enrollment secret wrong or expired; certificate ID already enrolled or revoked; CSR CommonName mismatch; server-side policy rejecting the request. The JSON response body often carries a reason field worth inspecting.","commonSituations":"Re-running enrollment with a one-time secret already consumed; secret rotated between generation and use; clock skew causing secret expiry; duplicate node ID joining the cluster.","solutions":["Inspect the response JSON (log it before this error is returned) for the server's reason string","Regenerate/obtain a fresh enrollment secret and retry with the same certificate ID","If the ID is taken, choose a new certificate ID or have the admin revoke the old entry first","Verify server-side enrollment logs for the denial cause"],"exampleFix":"// before\nif !response.OK {\n    return response, fmt.Errorf(\"home certificate request failed\")\n}\n\n// after (capture the server reason for diagnosis)\nif !response.OK {\n    return response, fmt.Errorf(\"home certificate request failed: ok=false error=%q\", response.Error)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"resp, err := requestCertificate(ctx, csrPEM)\nif err != nil {\n    if strings.Contains(err.Error(), \"home certificate request failed\") && resp.Error != \"\" {\n        log.Errorf(\"enrollment denied by server: %s\", resp.Error)\n    }\n    // retry only for transient causes (expired-then-refreshed secret), never for wrong-secret loops\n}","preventionTips":["Treat enrollment secrets as single-use: fetch a fresh one for each attempt","Log the full JSON response before returning the generic failure so denials are diagnosable","Use stable, unique certificate IDs per node to avoid duplicate-ID rejections"],"tags":["go","enrollment","resp","home","server-rejection"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}