DEV Community

Peyton Green
Peyton Green

Posted on

OpenAI Agents SDK 0.13 to 0.17: Three Breaking Changes You Will Hit

OpenAI Agents SDK 0.13 → 0.17: Three Breaking Changes You Will Hit

The OpenAI Agents SDK moved from 0.13 to 0.17 in five weeks (April 9 – May 19, 2026). That's 19 releases, including three breaking changes. Two of them are silent — they change runtime behavior without raising an error at upgrade time.

If you're running agents on 0.13.x, this is the migration guide you need before you upgrade.

The Three Breaks

Break #1: ModelRefusalError (v0.15.0) — Silent to Loud

The change: Model refusals used to return empty-string output. Now they raise an exception.

What this means: If your code treated refusals as output == "" or checked for empty responses, you were silent-failing gracefully. That behavior changed. A model refusal now raises ModelRefusalError and will crash your agent run unless you handle it.

What you need to do:

Register a "model_refusal" error handler before upgrading past 0.15.0:

from openai.agents import Agent, RunConfig

def handle_refusal(error):
    print(f"Model refused: {error.reason}")
    return "Model refused to respond. Try rephrasing."

agent = Agent(
    model="gpt-4",
    tools=[...],
)

config = RunConfig(
    on_error={
        "model_refusal": handle_refusal,
    }
)

response = agent.run("...", config=config)
Enter fullscreen mode Exit fullscreen mode

Without the handler, you'll see:

ModelRefusalError: model declined to process this request
Enter fullscreen mode Exit fullscreen mode

This is an intentional change — OpenAI wants refusals to be explicit, not silent. But it means your error handling has to change.

Impact: Any agent that doesn't add a refusal handler will crash on a refusal. High-risk if your agents field user input directly.


Break #2: Default Model Changed (v0.16.0) — Silent Model Switch

The change: The default model switched from gpt-4.1 to gpt-5.4-mini.

What this means: If you created an agent without explicitly setting model=, you were running on gpt-4.1. On upgrade to 0.16.0+, that same code silently switches to gpt-5.4-mini.

This isn't just a version bump — gpt-5.4-mini ships with different defaults:

  • reasoning.effort="none" (GPT-5 default, not OpenAI's historical "medium")
  • verbosity="low" (more concise output)
  • Different token economics (cheaper, but different quality/latency tradeoffs)

What you need to do:

Pin your model explicitly before upgrading:

from openai.agents import Agent, ModelSettings

agent = Agent(
    model="gpt-4.1",  # Explicit pin
    model_settings=ModelSettings(
        reasoning_effort="medium",  # Your baseline
    ),
    tools=[...],
)
Enter fullscreen mode Exit fullscreen mode

Or if you want to adopt gpt-5.4-mini intentionally:

agent = Agent(
    model="gpt-5.4-mini",
    model_settings=ModelSettings(
        reasoning_effort="medium",  # If you want reasoning
    ),
    tools=[...],
)
Enter fullscreen mode Exit fullscreen mode

The gotcha: If you have load tests, benchmarks, or quality baselines from 0.13.x, they won't be directly comparable to 0.16.0+ output without an explicit model pin. You'll think performance degraded when really the model changed.

Impact: High — your agents will produce different outputs, and you won't see an error. Your monitoring and quality thresholds need to account for this.


Break #3: Sandbox Source Paths (v0.17.0) — Existing Sandbox Code Only

The change: If you built Sandbox Agents in 0.14.x, the LocalFile.src and LocalDir.src paths must now resolve inside the materialization base_dir unless explicitly granted via Manifest.extra_path_grants.

Absolute paths outside base_dir now fail at runtime.

What this means: This only affects code that's already on 0.14.x sandboxes. If you're upgrading from 0.13.x (which didn't have Sandbox Agents), this doesn't apply to you. If you added sandboxes in 0.14.x, you need to audit your path handling.

What you need to do:

If you're using sandboxes with files outside the sandbox base directory:

from openai.agents import SandboxAgent, Manifest

manifest = Manifest(
    extra_path_grants=[
        "/path/to/external/data",
        "/shared/models",
    ]
)

agent = SandboxAgent(
    model="gpt-4",
    manifest=manifest,
    tools=[...],
)
Enter fullscreen mode Exit fullscreen mode

Or restructure to put everything inside base_dir.

Impact: Low if you're upgrading from 0.13.x. Medium if you're on 0.14.x with sandboxes.


Migration Checklist

Before upgrading to 0.15+:

  • [ ] Add a model_refusal error handler
  • [ ] Explicitly pin your model version
  • [ ] Test with your typical user inputs to catch refusal scenarios

If you're on 0.14.x sandboxes, before upgrading to 0.17.0:

  • [ ] Audit all LocalFile.src and LocalDir.src paths
  • [ ] Either move files into base_dir or add extra_path_grants

After upgrading:

  • [ ] Run your integration tests — the model change will affect output
  • [ ] Compare output quality against your 0.13.x baseline (pin the old model in parallel if you want a direct comparison)
  • [ ] Monitor refusal rates — they may spike depending on your use case

What's the Good News?

OpenAI shipped a massive capability in this window: Sandbox Agents (v0.14.0). They let your agent operate a filesystem, run code, persist snapshots, and mount remote storage (S3, GCS, Azure, etc.).

The following 15 releases were OpenAI hardening the sandbox implementation — fixing path security, session integrity, realtime support, and tracing resilience.

This means the SDK is maturing in the direction that matters: capability and reliability. The three breaks are uncomfortable, but they're the cost of OpenAI standardizing behavior (refusals should be loud, models should be explicit, sandboxes should be secure).


Code Example: Safe Upgrade

Here's a pattern that minimizes risk when upgrading:

from openai.agents import Agent, ModelSettings, RunConfig

def create_upgraded_agent(model="gpt-4.1"):
    """Safe agent factory for 0.15+ environments."""

    def handle_refusal(error):
        # Explicit refusal handling
        return f"Cannot proceed: {error.reason}"

    agent = Agent(
        model=model,  # Pinned, no silent switches
        model_settings=ModelSettings(
            reasoning_effort="medium",  # Explicit defaults
        ),
        tools=[...],
    )

    config = RunConfig(
        on_error={
            "model_refusal": handle_refusal,
        },
        max_turns=20,
    )

    return agent, config

# Use it
agent, config = create_upgraded_agent()
response = agent.run("...", config=config)
Enter fullscreen mode Exit fullscreen mode

This pattern:

  1. Pins the model (no surprises)
  2. Handles refusals explicitly (no crashes)
  3. Sets reasonable defaults (explicit config)
  4. Makes it easy to A/B test different models

The Bigger Picture

OpenAI shipped Sandbox Agents and spent 15 releases making them reliable. That's a real bet on capability + stability. The breaking changes are uncomfortable, but they're the cost of standardizing around behavior that should have been the baseline earlier.

If you're upgrading, treat it as a quality improvement with a migration cost — not a regression. The new defaults (gpt-5.4-mini, loud refusals, sandboxes) are better design. You just have to be explicit about it.


Next: If you're building multi-agent systems, watch the framework landscape. Google shipped ADK 2.0.0 stable + A2A 1.0.3 the same week OpenAI shipped this release. Different bets on what agents should do.

Top comments (0)