n4nAI

API integration

How-to

Error handling for OpenAI function calls in Python

Practical patterns for error handling OpenAI function calls Python: catch API failures, validate arguments, retry safely, and recover from model mistakes.

n4n Team3 min read700 words

Audio narration

Coming soon — every post will get a voice note here.

Robust error handling OpenAI function calls Python requires more than wrapping the SDK call in a bare try/except. The model can emit malformed arguments, the API can throttle or drop connections, and your local tool implementation can raise—each failure mode demands a distinct recovery path.

Step 1: Build a minimal function-calling loop

Start with the official openai package and a single tool. The loop sends messages, gets a response, and if the model requests a function, executes it and feeds the result back. Keep the initial version dumb: no retries, no validation. You need a known-good baseline before you can reason about failures.

from openai import OpenAI

client = OpenAI()  # reads OPENAI_API_KEY from env

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get current temperature for a city",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]

def get_weather(city: str) -> str:
    # stub: in reality this hits a weather API
    return f"22C in {city}"

messages = [{"role": "user", "content": "What's the weather in Tokyo?"}]

resp = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=messages,
    tools=tools,
)
msg = resp.choices[0].message

If msg.tool_calls is populated, you must append a tool role message with the matching tool_call_id before the next request. Effective error handling OpenAI function calls Python starts by classifying what actually breaks in this loop: the network, the API server, the model output, or your local function.

Step 2: Catch transport and API errors explicitly

The SDK raises a hierarchy of exceptions. Catch the specific ones you can recover from: RateLimitError, APITimeoutError, APIConnectionError. A APIStatusError with a 5xx status indicates server-side issues; 4xx (except 429) usually means your request is malformed or unauthorized.

from openai import (
    OpenAI,
    RateLimitError,
    APITimeoutError,
    APIConnectionError,
    APIStatusError,
)

def safe_create(client, **kwargs):
    try:
        return client.chat.completions.create(**kwargs)
    except RateLimitError as e:
        # inspect e.response.headers.get("retry-after")
        raise
    except (APITimeoutError, APIConnectionError) as e:
        # transient network issue, safe to retry
        raise
    except APIStatusError as e:
        if e.status_code >= 500:
            raise  # retry with backoff
        else:
            # 400/401/403/404: do not retry blindly
            print(f"Fatal API error {e.status_code}: {e.response}")
            raise

Never catch Exception broadly; it hides bugs in your own tool code. For error handling OpenAI function calls Python, surface the right exception to a retry layer and let fatal errors crash fast.

Step 3: Validate function arguments with a schema

Models hallucinate parameter shapes. A string field becomes a number, a required key vanishes. Use pydantic to parse the arguments JSON string. If validation fails, return a tool message describing the error so the model can self-correct on the next turn.

from pydantic import BaseModel, ValidationError

class WeatherArgs(BaseModel):
    city: str

def execute_tool_call(name: str, arguments: str) -> str:
    if name == "get_weather":
        try:
            args = WeatherArgs.model_validate_json(arguments)
        except ValidationError as e:
            return f"Invalid arguments: {e.errors()}. Provide a JSON object with a 'city' string."
        return get_weather(args.city)
    return f"Unknown function: {name}"

Feed the result back:

if msg.tool_calls:
    for call in msg.tool_calls:
        result = execute_tool_call(call.function.name, call.function.arguments)
        messages.append({
            "role": "tool",
            "tool_call_id": call.id,
            "content": result,
        })

This converts a parse failure from a crash into a second model turn. That loop is the core of error handling OpenAI function calls Python: let the model fix its own mistakes when the schema is violated.

Step 4: Handle unknown functions and refusals

If the model calls a function you didn’t register, do not invent a result. Return an explicit error string. Also watch finish_reason. A value of content_filter means the provider blocked the response; log it and return a safe fallback.

if msg.tool_calls:
    for call in msg.tool_calls:
        if call.function.name not in {"get_weather"}:
            messages.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": f"Function {call.function.name} is not available.",
            })
            continue
        # normal execution path
elif resp.choices[0].finish_reason == "content_filter":
    messages.append({"role": "assistant", "content": "I can't answer that."})

Unknown-function handling prevents the common production bug where a stale prompt schema drifts from your registered tools.

Step 5: Retry local tool execution with idempotency

The code inside your tool can fail independently of the LLM. Use tenacity to retry, but only if the tool is idempotent. For non-idempotent actions (payments, emails), require an idempotency_key. Generate one from tool_call_id so repeated calls with the same ID reuse the cached outcome.

from tenacity import retry, stop_after_attempt, wait_exponential

@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=10))
def get_weather_with_retry(city: str) -> str:
    # external call that may flap
    return get_weather(city)

def execute_idempotent(name, arguments, tool_call_id):
    if name == "get_weather":
        args = WeatherArgs.model_validate_json(arguments)
        cache_key = f"{name}:{tool_call_id}"
        if cache_key in _cache:
            return _cache[cache_key]
        out = get_weather_with_retry(args.city)
        _cache[cache_key] = out
        return out

That discipline separates robust error handling OpenAI function calls Python from naive scripts that double-charge a credit card on the third retry.

Step 6: Use a gateway for automatic provider fallback

A single provider outage should not 500 your app. An OpenAI-compatible endpoint like n4n.ai addresses 240+ models and performs automatic fallback when a provider is rate-limited or degraded, while honoring your routing directives and forwarding provider cache-control hints. Swap the base_url and keep the same code.

import os
from openai import OpenAI

client = OpenAI(
    base_url="https://api.n4n.ai/v1",
    api_key=os.environ["N4N_API_KEY"],
)

Because the interface is identical, the exception handling and validation above still apply; you gain resilience without rewriting retries. Per-token usage metering also lets you cap spend per request.

Step 7: Verify the full flow with an integration test

Write a pytest that exercises the happy path and a validation failure. Mock the API or point at a cheap model with a deterministic prompt. The test must prove that malformed arguments produce a recoverable tool message rather than an unhandled exception.

def test_validation_recovery(monkeypatch):
    import my_module
    bad = '{"city": 123}'   # pydantic rejects int
    good = '{"city": "Paris"}'

    # direct unit check on the executor
    r1 = my_module.execute_tool_call("get_weather", bad)
    assert "Invalid arguments" in r1
    r2 = my_module.execute_tool_call("get_weather", good)
    assert "Paris" in r2

    # simulate unknown function
    r3 = my_module.execute_tool_call("delete_db", "{}")
    assert "Unknown function" in r3

Run pytest -q. Success means: malformed arguments trigger a model-correctable error, unknown functions are reported, and API errors propagate to your retry layer instead of crashing the loop.

Verification checklist

  • Model returns valid JSON: tool executes, user gets answer.
  • Model returns wrong types: pydantic catches, model gets error, retries.
  • API returns 429/500: exception bubbles, caller backs off.
  • Provider down: gateway fails over, request succeeds.
  • Test suite passes locally and in CI.

That is the complete pattern for error handling OpenAI function calls Python in production. The key is to treat the model as a fallible client and your tools as untrusted input, then encode each failure mode as a typed branch instead of a generic catch-all.

Tagspythonopenaierror-handlingfunction-calling

Written by

n4n Team

The team building n4n — a single OpenAI-compatible API in front of 240+ models, with automatic fallback, load balancing and pay-per-token metering.

More from n4n Team →