{"record":{"id":"ea44ee4d57f6b6da","repo":"router-for-me/CLIProxyAPI","slug":"private-key-base64-payload-empty","errorCode":null,"errorMessage":"private_key base64 payload empty","messagePattern":"private_key base64 payload empty","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/auth/vertex/keyutil.go","lineNumber":140,"sourceCode":"\treturn nil, fmt.Errorf(\"private_key uses unsupported format\")\n}\n\nfunc rebuildPEM(raw string) (string, error) {\n\tkind := \"PRIVATE KEY\"\n\tif strings.Contains(raw, \"RSA PRIVATE KEY\") {\n\t\tkind = \"RSA PRIVATE KEY\"\n\t}\n\theader := \"-----BEGIN \" + kind + \"-----\"\n\tfooter := \"-----END \" + kind + \"-----\"\n\tstart := strings.Index(raw, header)\n\tend := strings.Index(raw, footer)\n\tif start < 0 || end <= start {\n\t\treturn \"\", fmt.Errorf(\"missing pem markers\")\n\t}\n\tbody := raw[start+len(header) : end]\n\tpayload := filterBase64(body)\n\tif payload == \"\" {\n\t\treturn \"\", fmt.Errorf(\"private_key base64 payload empty\")\n\t}\n\tder, err := base64.StdEncoding.DecodeString(payload)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"private_key base64 decode failed: %w\", err)\n\t}\n\tblock := &pem.Block{Type: kind, Bytes: der}\n\treturn string(pem.EncodeToMemory(block)), nil\n}\n\nfunc filterBase64(s string) string {\n\tvar b strings.Builder\n\tfor _, r := range s {\n\t\tswitch {\n\t\tcase r >= 'A' && r <= 'Z':\n\t\t\tb.WriteRune(r)\n\t\tcase r >= 'a' && r <= 'z':\n\t\t\tb.WriteRune(r)\n\t\tcase r >= '0' && r <= '9':","sourceCodeStart":122,"sourceCodeEnd":158,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/vertex/keyutil.go#L122-L158","documentation":"From rebuildPEM after the BEGIN/END markers were located, but the text between them yields no base64 characters after filterBase64 strips whitespace and invalid characters (keyutil.go:137-141). The PEM envelope exists while the actual key payload is empty.","triggerScenarios":"A PEM block containing only comments or an empty body; base64 body replaced by placeholder text like '<redacted>' or '***'; whitespace-only body between valid-looking markers.","commonSituations":"Sanitized/redacted credential templates deployed by mistake; secret managers substituting placeholders when access is denied; manual template editing that deletes the body but leaves headers.","solutions":["Check that the secret value is not a placeholder; fetch the real key material from GCP","Inspect the region between the BEGIN/END lines: jq -r .private_key sa.json | sed -n '2,3p' should show base64, not '***'","Re-download and redeploy the unmodified service account JSON"],"exampleFix":"# before\n\"private_key\": \"-----BEGIN PRIVATE KEY-----\\n***REDACTED***\\n-----END PRIVATE KEY-----\"\n# after\n\"private_key\": \"-----BEGIN PRIVATE KEY-----\\nMIIEvQ...(real base64)...\\n-----END PRIVATE KEY-----\"","handlingStrategy":"validation","validationCode":"body := pkBetweenMarkers(pk)\nif len(strings.Map(func(r rune) rune {\n    if isBase64Rune(r) { return r }\n    return -1\n}, body)) == 0 {\n    return fmt.Errorf(\"PEM body contains no base64 key material (redacted?)\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure secret managers return real values, not placeholders, before deploy","Scan credentials for redaction markers (***, <redacted>, null)","Integration-test the auth path with the actual production secret"],"tags":["vertex","pem","base64","redacted-secret"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}