{"record":{"id":"f3b662d2c59023bf","repo":"router-for-me/CLIProxyAPI","slug":"vertex-credential-service-account-content-is-empt","errorCode":null,"errorMessage":"vertex credential: service account content is empty","messagePattern":"vertex credential: service account content is empty","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"internal/auth/vertex/vertex_credentials.go","lineNumber":55,"sourceCode":"\n\t// Metadata holds arbitrary key-value pairs injected via hooks.\n\tMetadata map[string]any `json:\"-\"`\n}\n\n// SetMetadata allows external callers to inject metadata into the storage before saving.\nfunc (s *VertexCredentialStorage) SetMetadata(meta map[string]any) {\n\ts.Metadata = meta\n}\n\n// SaveTokenToFile writes the credential payload to the given file path in JSON format.\n// It ensures the parent directory exists and logs the operation for transparency.\nfunc (s *VertexCredentialStorage) SaveTokenToFile(authFilePath string) error {\n\tmisc.LogSavingCredentials(authFilePath)\n\tif s == nil {\n\t\treturn fmt.Errorf(\"vertex credential: storage is nil\")\n\t}\n\tif s.ServiceAccount == nil {\n\t\treturn fmt.Errorf(\"vertex credential: service account content is empty\")\n\t}\n\t// Ensure we tag the file with the provider type.\n\ts.Type = \"vertex\"\n\n\tif err := os.MkdirAll(filepath.Dir(authFilePath), 0o700); err != nil {\n\t\treturn fmt.Errorf(\"vertex credential: create directory failed: %w\", err)\n\t}\n\n\tdata, errMerge := misc.MergeMetadata(s, s.Metadata)\n\tif errMerge != nil {\n\t\treturn fmt.Errorf(\"vertex credential: merge metadata failed: %w\", errMerge)\n\t}\n\n\tf, err := os.Create(authFilePath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"vertex credential: create file failed: %w\", err)\n\t}\n\tdefer func() {","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/vertex/vertex_credentials.go#L37-L73","documentation":"Returned by VertexCredentialStorage.SaveTokenToFile when the storage's ServiceAccount map is nil (vertex_credentials.go:53-55). Saving a Vertex credential requires an actual service account payload; a zero-value storage (created but never populated) is rejected before any file I/O.","triggerScenarios":"Calling SaveTokenToFile on a newly constructed VertexCredentialStorage without setting ServiceAccount; a load path that failed to parse the JSON but continued with a nil map; programmatic embedding skipping the population step.","commonSituations":"SDK embedders constructing the storage manually; a partial import/migration that writes credential records before the account content is attached; tests exercising the save path with fixtures lacking the account.","solutions":["Populate ServiceAccount from a valid GCP service account JSON before saving (unmarshal the key file into the map)","Trace where the storage was built; if it comes from a loader, fix the loader so parse failures abort instead of producing nil","Use NormalizeServiceAccountMap first to validate, then assign"],"exampleFix":"// before\ns := &vertex.VertexCredentialStorage{}\nerr := s.SaveTokenToFile(path) // fails: service account content is empty\n// after\nvar sa map[string]any\nif err := json.Unmarshal(keyJSON, &sa); err != nil { return err }\ns.ServiceAccount = sa\nerr := s.SaveTokenToFile(path)","handlingStrategy":"validation","validationCode":"if s.ServiceAccount == nil {\n    return fmt.Errorf(\"refusing to save vertex credential without service account payload\")\n}","typeGuard":"func (s *VertexCredentialStorage) isPopulated() bool {\n    return s != nil && s.ServiceAccount != nil\n}","tryCatchPattern":null,"preventionTips":["Populate storage from a validated JSON key before saving","Abort load pipelines on parse failure instead of continuing with nil","Unit-test save paths with fully populated fixtures"],"tags":["vertex","service-account","validation","auth"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}