{"record":{"id":"0ccbaad9f1e436a4","repo":"JuliusBrussee/caveman","slug":"errgooglerequestcredentials","errorCode":"ErrGoogleRequestCredentials","errorMessage":"Google request credentials are invalid or conflicting","messagePattern":"Google request credentials are invalid or conflicting","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/google_credentials.go","lineNumber":12,"sourceCode":"package providers\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\n// ErrGoogleRequestCredentials contains no caller values and is safe to return\n// when equivalent Google authentication inputs disagree or are malformed.\nvar ErrGoogleRequestCredentials = errors.New(\"Google request credentials are invalid or conflicting\")\n\n// GoogleRequestAPIKey resolves the credential spellings Google documents: the\n// x-goog-api-key header and the key/$key system parameters. Nothing else counts\n// as a Google credential — in particular x-api-key is another provider's header,\n// so it neither conflicts with these nor selects a Google account here.\n//\n//\thttps://cloud.google.com/apis/docs/system-parameters\n//\thttps://ai.google.dev/gemini-api/docs/api-key\nfunc GoogleRequestAPIKey(req *http.Request) (string, error) {\n\tkey := strings.TrimSpace(req.Header.Get(\"x-goog-api-key\"))\n\tif strings.ContainsAny(key, \"\\r\\n\") {\n\t\treturn \"\", ErrGoogleRequestCredentials\n\t}\n\tfor _, part := range strings.Split(req.URL.RawQuery, \"&\") {\n\t\tname, value, _ := strings.Cut(part, \"=\")\n\t\tname, _ = url.QueryUnescape(name)\n\t\tif name != \"key\" && name != \"$key\" {\n\t\t\tcontinue","sourceCodeStart":1,"sourceCodeEnd":30,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/proxy/providers/google_credentials.go#L1-L30","documentation":"Google requests support exactly one credential spelling set: the x-goog-api-key header and key/$key system parameters. ErrGoogleRequestCredentials is a value-free sentinel returned when those equivalent inputs disagree or one is malformed, and the gateway maps it to HTTP 400 with code 'cave_provider_credentials_conflict'.","triggerScenarios":"ResolveUpstreamURL / header sanitization see x-goog-api-key and a ?key= (or $key) parameter carrying different values, a malformed key, or otherwise conflicting Google auth inputs on the same request.","commonSituations":"SDK clients that inject an API key header while the URL was built with a ?key= query param holding an older key; proxying clients configured for two different Google projects; leftover query params from copied example URLs.","solutions":["Ensure x-goog-api-key and any key/$key query parameter carry the identical API key, or remove one of them","Strip stale ?key=... query parameters from upstream URLs before forwarding","Check client SDK configuration for double credential injection (header plus query)","Replace the API key if it is malformed (Google keys are typically AIza... strings)"],"exampleFix":"// before (conflicting)\ncurl -H \"x-goog-api-key: KEY_A\" \"https://.../v1/models?key=KEY_B\"\n// after\n# drop the query param\ncurl -H \"x-goog-api-key: KEY_A\" \"https://.../v1/models\"\n","handlingStrategy":"validation","validationCode":"if h := r.Header.Get(\"x-goog-api-key\"); h != \"\" && r.URL.Query().Get(\"key\") != \"\" && h != r.URL.Query().Get(\"key\") {\n\t// conflict: strip or reconcile before sending\n}\n","typeGuard":null,"tryCatchPattern":"upstreamURL, err := adapter.ResolveUpstreamURL(ctx, r, providers.RouteContext{})\nif err != nil {\n\tif errors.Is(err, providers.ErrGoogleRequestCredentials) {\n\t\thttpx.Error(w, r, http.StatusBadRequest, \"cave_provider_credentials_conflict\", err.Error())\n\t\treturn\n\t}\n}\n","preventionTips":["Use one Google credential spelling per request (header or query, not both)","Strip ?key= params when a client already sends x-goog-api-key","Audit proxied client configs for double credential injection"],"tags":["google","api-key","conflict","http-400"],"backgroundTag":"conflicting-config-options","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}