{"record":{"id":"5d22791c4a9060d8","repo":"router-for-me/CLIProxyAPI","slug":"auth-id-is-empty","errorCode":null,"errorMessage":"auth id is empty","messagePattern":"auth id is empty","errorType":"http","errorClass":null,"httpStatus":500,"severity":"warning","filePath":"internal/api/handlers/management/config_apikey_disable.go","lineNumber":39,"sourceCode":"\t\treturn config.NormalizeExcludedModels(append(append([]string(nil), models...), configAPIKeyDisablePattern))\n\t}\n\tfiltered := make([]string, 0, len(models))\n\tfor _, item := range models {\n\t\tif strings.TrimSpace(item) == configAPIKeyDisablePattern {\n\t\t\tcontinue\n\t\t}\n\t\tfiltered = append(filtered, item)\n\t}\n\treturn config.NormalizeExcludedModels(filtered)\n}\n\nfunc toggleConfigAPIKeyExcludedAll(cfg *config.Config, auth *coreauth.Auth, disable bool) (bool, error) {\n\tif cfg == nil || auth == nil || !coreauth.IsConfigAPIKeyAuth(auth) {\n\t\treturn false, nil\n\t}\n\tauthID := strings.TrimSpace(auth.ID)\n\tif authID == \"\" {\n\t\treturn false, fmt.Errorf(\"auth id is empty\")\n\t}\n\n\tidGen := synthesizer.NewStableIDGenerator()\n\n\tfor i := range cfg.GeminiKey {\n\t\tentry := &cfg.GeminiKey[i]\n\t\tid, _ := idGen.Next(\"gemini:apikey\", entry.APIKey, entry.BaseURL)\n\t\tif id == authID {\n\t\t\tentry.ExcludedModels = setConfigAPIKeyExcludedAll(entry.ExcludedModels, disable)\n\t\t\treturn true, nil\n\t\t}\n\t}\n\tfor i := range cfg.InteractionsKey {\n\t\tentry := &cfg.InteractionsKey[i]\n\t\tid, _ := idGen.Next(\"gemini-interactions:apikey\", entry.APIKey, entry.BaseURL)\n\t\tif id == authID {\n\t\t\tentry.ExcludedModels = setConfigAPIKeyExcludedAll(entry.ExcludedModels, disable)\n\t\t\treturn true, nil","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/api/handlers/management/config_apikey_disable.go#L21-L57","documentation":"toggleConfigAPIKeyExcludedAll found a config-API-key auth (coreauth.IsConfigAPIKeyAuth true) whose ID is empty after trimming, so it cannot match the auth against entries in cfg.GeminiKey (and sibling API-key lists) that were assigned stable generated IDs. It errors instead of no-op'ing because the caller explicitly asked to toggle a config API key, and silently skipping would hide the mismatch.","triggerScenarios":"Calling the enable/disable (excluded-models) toggle on a synthetic config API key auth record that was constructed without an ID — test fixtures, custom embedders building auth objects directly, or records deserialized from a store that dropped the ID field.","commonSituations":"Programmatic auth construction bypassing the ID generator; store round-trips stripping the ID; forks changing ID derivation.","solutions":["GET the auth record and confirm its id field; re-create the config API key through normal flows so a stable ID is generated","Ensure custom code builds config API key auths with synthesizer.NewStableIDGenerator().Next(...) IDs","If IDs are being lost in storage, fix the store mapping for the ID field","Retry the toggle once the record carries a non-empty ID"],"exampleFix":"// before\nauth := &coreauth.Auth{Provider: \"gemini\", APIKey: \"...\"} // no ID\n// after\nid, _ := synthesizer.NewStableIDGenerator().Next(\"gemini:apikey\", key, baseURL)\nauth := &coreauth.Auth{ID: id, Provider: \"gemini\", APIKey: \"...\"}","handlingStrategy":"type-guard","validationCode":"if strings.TrimSpace(auth.ID) == \"\" {\n    return errors.New(\"config API key auth lacks an ID; regenerate the credential\")\n}","typeGuard":"func hasAuthID(auth *coreauth.Auth) bool { return auth != nil && strings.TrimSpace(auth.ID) != \"\" }","tryCatchPattern":null,"preventionTips":["Generate config API key auths only via the synthesizer so IDs are assigned","Verify IDs survive store round-trips in integration tests"],"tags":["validation","api-key","auth","id"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}