{"record":{"id":"d76136f0cd989915","repo":"JuliusBrussee/caveman","slug":"azure-request-has-duplicate-api-version-values","errorCode":null,"errorMessage":"azure request has duplicate api-version values","messagePattern":"azure request has duplicate api-version values","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/azureopenai/azure_routing.go","lineNumber":42,"sourceCode":"\t\"2025-04-01-preview\",\n\t\"preview\",\n\t\"latest\",\n}\n\n// ResolveUpstreamURL validates either the legacy deployment route or the\n// current Foundry Models v1 inference route before resolving upstream.\nfunc (a Adapter) ResolveUpstreamURL(ctx context.Context, req *http.Request, route providers.RouteContext) (*url.URL, error) {\n\tif err := validateAzureRequest(req.URL); err != nil {\n\t\treturn nil, err\n\t}\n\treturn a.Base.ResolveUpstreamURL(ctx, req, route)\n}\n\nfunc validateAzureRequest(u *url.URL) error {\n\tif foundryV1InferenceRoute(u.Path) {\n\t\tversions := u.Query()[\"api-version\"]\n\t\tif len(versions) > 1 {\n\t\t\treturn fmt.Errorf(\"azure request has duplicate api-version values\")\n\t\t}\n\t\tif len(versions) == 1 && versions[0] != \"v1\" && versions[0] != \"preview\" {\n\t\t\treturn fmt.Errorf(\"azure Foundry api-version %q is not supported\", versions[0])\n\t\t}\n\t\treturn nil\n\t}\n\tif !legacyChatCompletionsRoute(u.Path) {\n\t\treturn fmt.Errorf(\"azure legacy inference path %q is not supported\", u.Path)\n\t}\n\tversions := u.Query()[\"api-version\"]\n\tif len(versions) > 1 {\n\t\treturn fmt.Errorf(\"azure request has duplicate api-version values\")\n\t}\n\tversion := \"\"\n\tif len(versions) == 1 {\n\t\tversion = versions[0]\n\t}\n\tif version == \"\" {","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/providers/azureopenai/azure_routing.go#L24-L60","documentation":"On the Foundry Models v1 inference routes (/openai/v1/chat/completions, /openai/v1/responses), validateAzureRequest reads all api-version query parameters and rejects the request if more than one is present. Duplicate query keys make version selection ambiguous, so the proxy refuses rather than pick one. This guard runs in ResolveUpstreamURL before any upstream dial.","triggerScenarios":"A URL like /azure/openai/v1/chat/completions?api-version=v1&api-version=preview — usually caused by a client SDK appending api-version while the caller also set it, or a URL-builder appending the parameter twice.","commonSituations":"Combining an SDK that auto-appends api-version with hand-added query params; retry/rewrite middleware that appends the param without checking; string concatenation building URLs in scripts.","solutions":["Send exactly one api-version (for Foundry v1 routes: 'v1' or 'preview').","Audit the client for double-appending: disable the SDK's automatic api-version or remove your manual query parameter.","Build URLs with a params map (key set once) instead of string concatenation."],"exampleFix":"# before\ncurl \"$PROXY/azure/openai/v1/chat/completions?api-version=v1&api-version=preview\"\n\n# after\ncurl \"$PROXY/azure/openai/v1/chat/completions?api-version=v1\"","handlingStrategy":"validation","validationCode":"vals := req.URL.Query()[\"api-version\"]\nif len(vals) > 1 {\n    return fmt.Errorf(\"duplicate api-version values %v; send exactly one\", vals)\n}","typeGuard":"func hasSingleAPIVersion(u *url.URL) bool {\n    return len(u.Query()[\"api-version\"]) <= 1\n}","tryCatchPattern":"if err := validateAzureRequest(req.URL); err != nil {\n    if strings.Contains(err.Error(), \"duplicate api-version\") {\n        http.Error(w, \"send exactly one api-version query parameter\", http.StatusBadRequest)\n        return\n    }\n    http.Error(w, err.Error(), http.StatusBadRequest)\n}","preventionTips":["Set api-version in exactly one layer (SDK config OR manual param, never both).","Build URLs from a param map, not string concatenation.","Log RawQuery at the edge to catch double-appending middleware."],"tags":["go","azure","validation","query-params"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}