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

  1. Provide a real Google Cloud service account JSON key file (download from GCP IAM & Admin > Service Accounts > Keys)
  2. Point the Vertex credential config/path at that file and verify it parses: cat file | jq .type should equal service_account
  3. 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

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


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/13b905b21ae78d5f. Report an issue: GitHub.