router-for-me/CLIProxyAPI · error
service account payload is empty
Error message
service account payload is empty
What it means
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.
Source
Thrown at internal/auth/vertex/keyutil.go:39
if err := json.Unmarshal(raw, &payload); err != nil {
return raw, err
}
normalized, err := NormalizeServiceAccountMap(payload)
if err != nil {
return raw, err
}
out, err := json.Marshal(normalized)
if err != nil {
return raw, err
}
return out, nil
}
// NormalizeServiceAccountMap returns a copy of the given service account map with
// a sanitized private_key field that is guaranteed to contain a valid RSA PRIVATE KEY PEM block.
func NormalizeServiceAccountMap(sa map[string]any) (map[string]any, error) {
if sa == nil {
return nil, fmt.Errorf("service account payload is empty")
}
pk, _ := sa["private_key"].(string)
if strings.TrimSpace(pk) == "" {
return nil, fmt.Errorf("service account missing private_key")
}
normalized, err := sanitizePrivateKey(pk)
if err != nil {
return nil, err
}
clone := make(map[string]any, len(sa))
for k, v := range sa {
clone[k] = v
}
clone["private_key"] = normalized
return clone, nil
}
func sanitizePrivateKey(raw string) (string, error) {View on GitHub (pinned to 78f0c4079e)
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
Example fix
// before
normalized, err := vertex.NormalizeServiceAccountMap(sa) // sa may be nil
// after
if len(sa) == 0 {
return fmt.Errorf("vertex: no service account payload provided; supply a service account JSON key")
}
normalized, err := vertex.NormalizeServiceAccountMap(sa) Defensive patterns
Strategy: validation
Validate before calling
if len(sa) == 0 {
return fmt.Errorf("no service account payload: provide a GCP service account JSON key")
} Type guard
func hasServiceAccountPayload(sa map[string]any) bool {
return len(sa) > 0
} Try / catch
if _, err := vertex.NormalizeServiceAccountMap(sa); err != nil {
if strings.Contains(err.Error(), "payload is empty") {
return failConfig("vertex service account JSON missing or 'null'")
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- service account missing private_key
- vertex credential: service account content is empty
- private_key pem decode failed
- count must be a positive integer
- Codex live multipart boundary is missing
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/13b905b21ae78d5f.
Report an issue: GitHub.