Howwise RSS
AI & Automation6 min read

Fix Azure OpenAI 'Unrecognized request argument: functions'

Short answer

The Azure OpenAI unrecognized request argument error for functions means your api-version or deployed model version does not support function calling. Use api-version 2024-06-01 or newer with a gpt-35-turbo 0613+, gpt-4 0613+ or gpt-4o deployment.

Key takeaways
  • The 400 error is about the Azure endpoint, api-version or model version, not about the shape of your JSON.
  • Function calling needs api-version 2023-07-01-preview or later and a model such as gpt-35-turbo 0613+, gpt-4 0613+ or gpt-4o.
  • Deployments on model versions 0301 or 0314 never accept functions or tools, so they must be redeployed on a newer version.
  • Migrate from functions/function_call to tools/tool_choice; the same two causes explain 'Unrecognized request argument supplied: tools'.
  • Pass the deployment name as model, set api_version on AzureOpenAI, and in LangChain use AzureChatOpenAI with bind_tools.

Why does Azure OpenAI say "Unrecognized request argument supplied: functions"?

The Azure OpenAI unrecognized request argument error appears because Azure validates every request body against the schema of the api-version in the query string, then against the capabilities of the model deployment. If either one has no `functions` field, the request is rejected with HTTP 400 and the message "Unrecognized request argument supplied: functions". Your JSON is not malformed; the endpoint simply does not know that field.

Function calling was added to the Azure chat completions API in api-version 2023-07-01-preview. Older versions such as 2022-12-01 and 2023-03-15-preview predate it, and models deployed from the 0301 or 0314 snapshots were trained without it. Code copied from the OpenAI docs usually works against api.openai.com because OpenAI has no api-version concept, which is why the failure only appears on Azure.

The same logic applies to `tools`. If you switch to the newer parameter and get "Unrecognized request argument supplied: tools", you are hitting the same two causes, not a new problem.

Symptom to cause to fix: a quick diagnosis table

Work down this table from the top. The first two rows explain most reports of this error.

SymptomLikely causeFix
400 'Unrecognized request argument supplied: functions' or 'tools'api-version older than 2023-07-01-previewSet api_version to 2024-06-01 or newer
Same error with a new api-versionDeployment uses gpt-35-turbo 0301 or gpt-4 0314Redeploy or upgrade to 0613+ (or gpt-4o and later)
404 DeploymentNotFoundPassed the OpenAI model name instead of the deployment nameUse model=<your deployment name>
Error only through a gateway or APIMGateway pins an old api-version or rewrites the URLCheck the policy and backend URL, then test the resource directly
Error only in LangChainUsing AzureOpenAI (completions class) or an old versionUse AzureChatOpenAI with azure_deployment and api_version
400 on response_format json_schema or parallel_tool_callsapi-version or model too old for that featureUse 2024-08-01-preview or later with a model that supports it
Error on /completions pathCalling the completions endpoint, not chat completionsUse client.chat.completions.create

How do I set the api-version and Azure client correctly?

With openai 1.0 or newer, create an `AzureOpenAI` client and set `api_version` explicitly. The old `openai.api_type = 'azure'` globals belong to the 0.x SDK, which is also where `engine=` and `deployment_id=` came from. If `pip show openai` reports 0.x, upgrade with `pip install -U openai` and migrate your calls.

In LangChain, `api_version` can also come from the `OPENAI_API_VERSION` environment variable. A stale value exported in your shell or container image is a common reason a fix in code seems to be ignored.

from openai import AzureOpenAI

client = AzureOpenAI(
    azure_endpoint="https://YOUR-RESOURCE.openai.azure.com",
    api_key="...",
    api_version="2024-06-01",   # not 2023-03-15-preview or 2022-12-01
)

# Legacy 0.x SDK, for reference only:
# openai.api_type = "azure"
# openai.api_base = "https://YOUR-RESOURCE.openai.azure.com"
# openai.api_version = "2023-07-01-preview"
# openai.ChatCompletion.create(engine="my-deployment", ...)

How do I check the deployed model version?

The api-version only unlocks the field; the deployment must also support the feature. Function calling needs gpt-35-turbo 0613 or later, gpt-4 0613 or later, or gpt-4o and later models. A deployment on 0301 or 0314 rejects `functions` and `tools` whatever the api-version.

Azure also expects the deployment name in the `model` argument. That name is whatever you typed when you created the deployment, and it may differ from the underlying model name.

  1. Open the Azure portal, go to your Azure OpenAI resource, and open the deployments list (Azure AI Foundry or Azure OpenAI Studio).
  2. Find your deployment and read its model name and model version columns.
  3. If the version is 0301 or 0314, create a new deployment on 0613 or later, or on gpt-4o or newer, and use its name as `model`.
  4. If the model is not offered in your region, pick a region that lists it; new versions often reach some regions later than others.
  5. Re-run the request and confirm the 400 is gone.
Warning: Changing the model version of an existing deployment, or deleting and recreating it, can change behaviour, quotas and outputs for every app using that deployment name. Create a new deployment and switch traffic over rather than editing the production one.

Legacy functions vs modern tools: before and after code

`functions` and `function_call` are deprecated in favour of `tools` and `tool_choice`. Each tool is wrapped as `{"type": "function", "function": {...}}`. Both forms work on Azure when the api-version and model allow them, but new code should use `tools`.

The schema in `parameters` must be valid JSON Schema with `type: object`, a `properties` map and a `required` list. The `description` fields are what the model reads when it decides which function to call, so write them precisely. Define the `tools` list once as a variable, as below, so every later request in the conversation can reuse it.

import json

schema = {
    "type": "object",
    "properties": {
        "city": {"type": "string", "description": "City name, e.g. Paris"},
        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
    },
    "required": ["city"],
}
messages = [{"role": "user", "content": "Weather in Paris?"}]

# BEFORE (legacy)
resp = client.chat.completions.create(
    model="my-gpt4o-deployment",
    messages=messages,
    functions=[{"name": "get_weather", "description": "Get current weather", "parameters": schema}],
    function_call="auto",
)

# AFTER (current): define the list once and reuse it in every request
tools = [{"type": "function", "function": {
    "name": "get_weather",
    "description": "Get current weather for a city",
    "parameters": schema}}]

resp = client.chat.completions.create(
    model="my-gpt4o-deployment",   # deployment name
    messages=messages,
    tools=tools,
    tool_choice="auto",
)

How do I parse the tool call and send the result back?

The new response puts calls in `message.tool_calls`, while the legacy form uses `message.function_call`. The `arguments` value is a JSON string, not a dict, and the model can occasionally emit invalid JSON or omit a required key. Parse it defensively and validate before running anything with side effects.

Reply with one message of role `tool` per call, each carrying the matching `tool_call_id`. The legacy equivalent was role `function` with a `name`. Append the assistant message first, or the API rejects the sequence. This snippet continues from the previous one and reuses its `client`, `messages`, `tools` and `resp`.

# Continues the previous example: client, messages, tools and resp are already defined.
msg = resp.choices[0].message
if msg.tool_calls:
    messages.append(msg)  # assistant message containing all tool_calls
    for call in msg.tool_calls:
        try:
            args = json.loads(call.function.arguments)
        except json.JSONDecodeError:
            args = None
        if call.function.name != "get_weather" or not args or "city" not in args:
            result = {"error": "invalid arguments"}
        else:
            result = {"city": args["city"], "temp_c": 18}  # your real function here

        messages.append({
            "role": "tool",
            "tool_call_id": call.id,   # one tool message per call
            "content": json.dumps(result),
        })

    # Only after every tool_call_id has been answered, ask for the final reply
    final = client.chat.completions.create(
        model="my-gpt4o-deployment", messages=messages, tools=tools)
    print(final.choices[0].message.content)
Tip: Models can return several tool_calls in one turn when parallel tool calls are available. The loop above answers each tool_call_id; skipping any one of them makes the next request fail with a 400.

LangChain, gateways and a minimal curl test

In LangChain use `AzureChatOpenAI`, not `AzureOpenAI`, which wraps the completions endpoint. Pass `azure_deployment` and `api_version`, then call `bind_tools` instead of building `functions` by hand. If it still fails, test the raw endpoint, because an API Management gateway or proxy can pin an old api-version or forward to a different backend.

Structured outputs and parallel tool calls need newer versions. As a rule of thumb, use 2024-08-01-preview or later with a model that supports the feature, and treat anything older as unsupported for them.

# LangChain
# from langchain_openai import AzureChatOpenAI
# llm = AzureChatOpenAI(azure_deployment="my-gpt4o-deployment",
#     api_version="2024-06-01", azure_endpoint="https://YOUR-RESOURCE.openai.azure.com")
# llm_with_tools = llm.bind_tools([get_weather])

# Minimal curl test (the URL shows the api-version being sent)
curl -i "https://YOUR-RESOURCE.openai.azure.com/openai/deployments/my-gpt4o-deployment/chat/completions?api-version=2024-06-01" \
  -H "api-key: $AZURE_OPENAI_API_KEY" -H "Content-Type: application/json" \
  -d '{"messages":[{"role":"user","content":"Weather in Paris?"}],"tools":[{"type":"function","function":{"name":"get_weather","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}}],"tool_choice":"auto"}'
Tip: If curl succeeds but your app fails, the difference is in your client config, an environment variable or a gateway. If curl fails too, the cause is the deployment or api-version.

Common questions

Which Azure OpenAI api-version should I use for function calling?

The minimum is 2023-07-01-preview, but use a current GA version such as 2024-06-01 or newer. Features like structured outputs and parallel tool calls need newer versions such as 2024-08-01-preview or later.

Does gpt-35-turbo support function calling on Azure?

Yes, but only from model version 0613 onward. Deployments on 0301 reject both functions and tools, so redeploy on a newer version.

Can I still use functions instead of tools on Azure?

It works on supported api-versions and models, but it is deprecated. Move to tools and tool_choice, since new features are only added to that form.

Why do I get a 404 instead of the unrecognized argument error?

A 404 DeploymentNotFound usually means you passed the OpenAI model name rather than your deployment name, or the endpoint belongs to a different resource. Check the deployment name in the portal and the endpoint URL.

Why does the error persist after I changed api_version in code?

An OPENAI_API_VERSION environment variable, a gateway policy or a cached config may override it. Print the final request URL and confirm the api-version query string.

Related topics:azure openaifunction callingopenai apilangchain