{"record":{"id":"fb7efcd57f210703","repo":"JuliusBrussee/caveman","slug":"azure-request-missing-api-version","errorCode":null,"errorMessage":"azure request missing api-version","messagePattern":"azure request missing api-version","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/azureopenai/azure_routing.go","lineNumber":61,"sourceCode":"\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 == \"\" {\n\t\treturn fmt.Errorf(\"azure request missing api-version\")\n\t}\n\tif !apiVersionAllowed(version) {\n\t\treturn fmt.Errorf(\"azure api-version %q is not on the allowlist\", version)\n\t}\n\treturn nil\n}\n\nfunc foundryV1InferenceRoute(path string) bool {\n\tpath = strings.TrimPrefix(path, \"/azure\")\n\tswitch path {\n\tcase \"/openai/v1/chat/completions\", \"/openai/v1/responses\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc legacyChatCompletionsRoute(path string) bool {","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/providers/azureopenai/azure_routing.go#L43-L79","documentation":"On the legacy deployment route, api-version is mandatory: Azure's legacy surface requires the dated query parameter to select API behavior, and this proxy enforces presence (after duplicate checks) before resolving upstream. A legacy request with no api-version at all fails with this error rather than being forwarded and rejected upstream.","triggerScenarios":"Calling /azure/openai/deployments/gpt-4o/chat/completions with no query string — e.g. a client that assumes the proxy injects the version, or a URL from which the query was accidentally dropped.","commonSituations":"Assuming the v1-route convention (version optional) applies to the legacy route; URL templating that omits empty parameters; migrating configs where the version lived in a different field that went unset.","solutions":["Append a dated, allowlisted version: ?api-version=2024-10-21.","Or switch to the Foundry v1 route where api-version may be omitted entirely.","Centralize version selection in client config so it is never accidentally empty."],"exampleFix":"# before\ncurl \"$PROXY/azure/openai/deployments/gpt-4o/chat/completions\"\n\n# after\ncurl \"$PROXY/azure/openai/deployments/gpt-4o/chat/completions?api-version=2024-10-21\"","handlingStrategy":"validation","validationCode":"if strings.HasPrefix(strings.TrimPrefix(u.Path, \"/azure\"), \"/openai/deployments/\") {\n    if u.Query().Get(\"api-version\") == \"\" {\n        return errors.New(\"legacy Azure route requires ?api-version=<dated version>\")\n    }\n}","typeGuard":"func legacyRouteHasVersion(u *url.URL) bool {\n    legacy := strings.HasPrefix(strings.TrimPrefix(u.Path, \"/azure\"), \"/openai/deployments/\")\n    return !legacy || u.Query().Get(\"api-version\") != \"\"\n}","tryCatchPattern":"if err := validateAzureRequest(req.URL); err != nil {\n    if strings.Contains(err.Error(), \"missing api-version\") {\n        http.Error(w, \"legacy route requires api-version, e.g. ?api-version=2024-10-21 (or use the /openai/v1 route)\", http.StatusBadRequest)\n        return\n    }\n    http.Error(w, err.Error(), http.StatusBadRequest)\n}","preventionTips":["Remember version rules differ per route: mandatory on legacy, optional on v1.","Never let URL templating drop empty params silently.","Smoke-test Azure routes in CI with a URL linter."],"tags":["go","azure","validation","query-params"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}