{"record":{"id":"36fe5742ced5470c","repo":"JuliusBrussee/caveman","slug":"azure-legacy-inference-path-q-is-not-supported","errorCode":null,"errorMessage":"azure legacy inference path %q is not supported","messagePattern":"azure legacy inference path %q is not supported","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/providers/azureopenai/azure_routing.go","lineNumber":50,"sourceCode":"\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 == \"\" {\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","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/proxy/providers/azureopenai/azure_routing.go#L32-L68","documentation":"Any Azure request whose path is neither a Foundry v1 inference route (/openai/v1/chat/completions, /openai/v1/responses) nor the legacy deployment route (/openai/deployments/<name>/chat/completions with a syntactically valid deployment name) is rejected with this error echoing the offending path. The proxy only forwards the two known-good Azure surfaces, closing the door on typos, unsupported previews, and path-injection attempts.","triggerScenarios":"Calling /azure/openai/deployments/my-dep/completions (missing 'chat' segment), /azure/openai/embeddings, a five-segment path whose deployment contains illegal characters, or a v1 route with a typo like /openai/v1/chat/completion.","commonSituations":"Pointing a generic OpenAI SDK at the /azure prefix so paths like /v1/models or /embeddings arrive; typo'd route; expecting embeddings or other Azure features that this surface does not proxy; deployment names with spaces or slashes.","solutions":["Use one of the two supported shapes: /azure/openai/v1/chat/completions (Foundry, api-version=v1) or /azure/openai/deployments/<deployment>/chat/completions?api-version=<date>.","Fix the path typo — compare segment by segment against the working shapes above.","If you need embeddings/other endpoints, route them directly to Azure; this proxy surface is chat/completions-only by design."],"exampleFix":"# before\ncurl \"$PROXY/azure/openai/deployments/gpt-4o/completions?api-version=2024-10-21\"\n\n# after\ncurl \"$PROXY/azure/openai/deployments/gpt-4o/chat/completions?api-version=2024-10-21\"","handlingStrategy":"validation","validationCode":"p := strings.TrimPrefix(u.Path, \"/azure\")\nfoundry := p == \"/openai/v1/chat/completions\" || p == \"/openai/v1/responses\"\nparts := strings.Split(strings.Trim(p, \"/\"), \"/\")\nlegacy := len(parts) == 5 && parts[0] == \"openai\" && parts[1] == \"deployments\" &&\n    parts[3] == \"chat\" && parts[4] == \"completions\" &&\n    validDeploymentName(parts[2])\nif !foundry && !legacy {\n    return fmt.Errorf(\"unsupported Azure path %q; use /openai/v1/... or /openai/deployments/<name>/chat/completions\", u.Path)\n}","typeGuard":"func isSupportedAzurePath(path string) bool {\n    p := strings.TrimPrefix(path, \"/azure\")\n    if p == \"/openai/v1/chat/completions\" || p == \"/openai/v1/responses\" {\n        return true\n    }\n    parts := strings.Split(strings.Trim(p, \"/\"), \"/\")\n    return len(parts) == 5 && parts[0] == \"openai\" && parts[1] == \"deployments\" &&\n        parts[3] == \"chat\" && parts[4] == \"completions\"\n}","tryCatchPattern":"if err := validateAzureRequest(req.URL); err != nil {\n    if strings.Contains(err.Error(), \"legacy inference path\") {\n        http.Error(w, \"unsupported Azure route; see /openai/v1/chat/completions and /openai/deployments/<dep>/chat/completions\", http.StatusNotFound)\n        return\n    }\n    http.Error(w, err.Error(), http.StatusBadRequest)\n}","preventionTips":["Template Azure client URLs from the two canonical route shapes.","Do not point generic OpenAI SDKs (models/embeddings endpoints) at the /azure prefix.","Keep deployment names to [A-Za-z0-9._-], max 128 chars."],"tags":["go","azure","routing","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}