{"record":{"id":"13b905b21ae78d5f","repo":"router-for-me/CLIProxyAPI","slug":"service-account-payload-is-empty","errorCode":null,"errorMessage":"service account payload is empty","messagePattern":"service account payload is empty","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/auth/vertex/keyutil.go","lineNumber":39,"sourceCode":"\tif err := json.Unmarshal(raw, &payload); err != nil {\n\t\treturn raw, err\n\t}\n\tnormalized, err := NormalizeServiceAccountMap(payload)\n\tif err != nil {\n\t\treturn raw, err\n\t}\n\tout, err := json.Marshal(normalized)\n\tif err != nil {\n\t\treturn raw, err\n\t}\n\treturn out, nil\n}\n\n// NormalizeServiceAccountMap returns a copy of the given service account map with\n// a sanitized private_key field that is guaranteed to contain a valid RSA PRIVATE KEY PEM block.\nfunc NormalizeServiceAccountMap(sa map[string]any) (map[string]any, error) {\n\tif sa == nil {\n\t\treturn nil, fmt.Errorf(\"service account payload is empty\")\n\t}\n\tpk, _ := sa[\"private_key\"].(string)\n\tif strings.TrimSpace(pk) == \"\" {\n\t\treturn nil, fmt.Errorf(\"service account missing private_key\")\n\t}\n\tnormalized, err := sanitizePrivateKey(pk)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclone := make(map[string]any, len(sa))\n\tfor k, v := range sa {\n\t\tclone[k] = v\n\t}\n\tclone[\"private_key\"] = normalized\n\treturn clone, nil\n}\n\nfunc sanitizePrivateKey(raw string) (string, error) {","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/vertex/keyutil.go#L21-L57","documentation":"Thrown by NormalizeServiceAccountMap when the parsed service account map is nil (internal/auth/vertex/keyutil.go:38-39). The Vertex AI credential path requires a Google service account JSON object; a nil map means the caller passed an empty/absent payload where one was mandatory.","triggerScenarios":"A Vertex credential flow calling NormalizeServiceAccountMap(nil) or with a JSON literal 'null'; a decoded file/env variable that contained only whitespace or 'null'; code that unmarshals a missing GOOGLE_APPLICATION_CREDENTIALS file into an uninitialized map and proceeds anyway.","commonSituations":"Configuring vertex auth without actually providing service-account.json; pointing credential-file config at an empty or corrupted file; env var expansion producing an empty string that later parses as null.","solutions":["Provide a real Google Cloud service account JSON key file (download from GCP IAM & Admin > Service Accounts > Keys)","Point the Vertex credential config/path at that file and verify it parses: cat file | jq .type should equal service_account","If embedding the SDK, guard for empty input before calling NormalizeServiceAccountMap and fail early with a clear message"],"exampleFix":"// before\nnormalized, err := vertex.NormalizeServiceAccountMap(sa) // sa may be nil\n// after\nif len(sa) == 0 {\n    return fmt.Errorf(\"vertex: no service account payload provided; supply a service account JSON key\")\n}\nnormalized, err := vertex.NormalizeServiceAccountMap(sa)","handlingStrategy":"validation","validationCode":"if len(sa) == 0 {\n    return fmt.Errorf(\"no service account payload: provide a GCP service account JSON key\")\n}","typeGuard":"func hasServiceAccountPayload(sa map[string]any) bool {\n    return len(sa) > 0\n}","tryCatchPattern":"if _, err := vertex.NormalizeServiceAccountMap(sa); err != nil {\n    if strings.Contains(err.Error(), \"payload is empty\") {\n        return failConfig(\"vertex service account JSON missing or 'null'\")\n    }\n    return err\n}","preventionTips":["Validate the credential file exists and is non-empty before parsing","Check jq .type == \"service_account\" at deploy time","Fail fast in config loading when the vertex block has no key source"],"tags":["vertex","gcp","service-account","validation"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}