fix: strip model from Azure request body after URL construction#2972
fix: strip model from Azure request body after URL construction#2972alvinttang wants to merge 2 commits intoopenai:mainfrom
Conversation
When using AzureOpenAI, the _build_request method uses the model parameter
from the JSON body to construct the deployment URL path, but leaves it in
the body. Azure validates this field against actual model names, causing
failures when the deployment name differs (e.g. 'gpt-image-1-5' vs
'gpt-image-1.5').
Change .get('model') to .pop('model', None) so the model parameter is
removed from the request body after being used for URL routing.
Fixes openai#2892
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7f0456c1dc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
src/openai/lib/azure.py
Outdated
| ) -> httpx.Request: | ||
| if options.url in _deployments_endpoints and is_mapping(options.json_data): | ||
| model = options.json_data.get("model") | ||
| model = options.json_data.pop("model", None) |
There was a problem hiding this comment.
Avoid mutating shared json_data when extracting model
Using pop("model", None) here mutates the same json_data mapping that is reused across retries, because BaseClient.request() clones FinalRequestOptions with shallow model_copy() (src/openai/_base_client.py lines 974-987 and src/openai/_compat.py line 122). After the first attempt, retries no longer have model, so _build_request stops prepending /deployments/{model} and sends the retry to a non-deployment URL (e.g. /chat/completions), which can make transient failures unrecoverable for Azure deployment-based calls.
Useful? React with 👍 / 👎.
Replace `json_data.pop("model")` with `.get()` + dict comprehension so
the original mapping is not mutated. This prevents retries from losing
the `model` key (and therefore sending requests to the wrong URL) when
`BaseClient.request()` reuses a shallow-copied options object.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
_build_requestinBaseAzureClientto use.pop("model", None)instead of.get("model"), removing themodelparameter from the JSON request body after it has been used to construct the/deployments/{model}/...URL path.gpt-image-1-5deployment for modelgpt-image-1.5).Fixes #2892
Root Cause
In
src/openai/lib/azure.py,_build_requestreads themodelfield fromoptions.json_datato build the deployment URL but does not remove it. The Azure backend then validates the body'smodelvalue against known model names, rejecting deployment names likegpt-image-1-5that don't match exactly (Azure naming rules prohibit dots in deployment names).Fix
One-line change:
options.json_data.get("model")->options.json_data.pop("model", None)This removes
modelfrom the body after extracting it for URL construction, which aligns with the Azure OpenAI REST API spec where model selection is done entirely via the URL path for deployment-based endpoints.Test plan
modelin the body was always redundant for Azure deployment endpoints)gpt-image-1-5forgpt-image-1.5)🤖 Generated with Claude Code