Fehler bei OpenAI Agents SDK mit RedisSession im Redis Cluster behoben
Bei der Kombination von OpenAI Agents SDK und Redis Cluster tritt der Fehler auf, dass dem ClusterPipeline-Objekt das Attribut raise_first_error fehlt. Fuer openai-agents 0.24.0 mit redis 8.1.0 gibt es nun Loesungsansaetze aus dem offiziellen GitHub-Issue und dem SDK-Quellcode.

Veröffentlicht am 25. Oktober • Zuerst erschienen auf memorysync.hashnode.dev
Offenlegung: Ich bin Mohammed, Gründer von MemorySync. Diesen Text habe ich mit KI-Unterstützung auf Basis des öffentlichen GitHub-Issues und des SDK-Quellcodes verfasst. Zum Stand des Codes: Am 25. Oktober 2026 installierte ich openai-agents 0.24.0 und redis 8.1.0 (Python 3.13). Ich startete einen lokalen Redis-Cluster mit drei Knoten (Redis 8.0.2), stellte fest, dass ClusterPipeline die erwarteten Attribute fehlen, reproduzierte den neuen TypeError aus add_items und clear_session mit einem RedisCluster-Client und testete die unten stehende eigene Session gegen diesen Cluster, einschließlich zweier Runner.run-Durchläufe mit einem Platzhaltermodell (kein OpenAI-Schlüssel vorhanden, also kein echter LLM-Aufruf). Die Hash-Slot-Berechnung prüfte ich mit key_slot aus redis-py.
Mit der Redis-basierten Session des OpenAI Agents SDK und einem Cluster-Client ( redis.asyncio.cluster.RedisCluster ) schlägt das Speichern des Verlaufs fehl:
File "agents/extensions/memory/redis_session.py", line 483, in _write_items_attempt raise_first_error = pipe.raise_first_error AttributeError: 'ClusterPipeline' object has no attribute 'raise_first_error' Enter fullscreen mode Exit fullscreen mode Dies ist openai/openai-agents-python#5322, das zum 25. Oktober 2026 noch offen ist. Der Melder trat dem Fehler von Version 0.20.0 bis 0.23.1 auf. Laut Issue schlagen add_items und (in der neuesten Version) clear_session fehl, während get_items und pop_item funktionieren, und in 0.14.5 funktionierten alle vier Operationen.
In Version 0.24.0 erhält man stattdessen eine klarere Fehlermeldung. Ich habe dies gegen einen lokalen Redis-Cluster reproduziert:
TypeError: RedisSession does not support transactions with RedisCluster clients. Use redis.asyncio.Redis connected to a standalone Redis server, or implement a custom Session backend for Redis Cluster. Enter fullscreen mode Exit fullscreen mode In 0.24.0 sitzt die Prüfung im Schreibpfad und nicht im __init__. Das Anlegen der Session gelingt, und der erste Schreibvorgang löst den Fehler aus. Das stammt aus PR #5361 ("scope Redis Cluster rejection to transactional writes"), den das Issue verlinkt und der am 14. Oktober 2026 gemergt wurde. Auf dem Cluster funktionierten get_items und pop_item auch in 0.24.0 weiterhin, während add_items und clear_session den TypeError auslösten.
A. RedisSession auf einen eigenständigen (nicht geclusterten) Redis-Server richten. Das ist es, was die Fehlermeldung des SDK vorschlägt. Es ist am einfachsten, wenn dein Anbieter einen Nicht-Cluster-Modus anbietet.
B. openai-agents==0.14.5 pinnen. Das Issue berichtet, dass dies auf einem Cluster funktionierte, und auf meinem lokalen Cluster (mit redis 8.1.0) liefen add, get, pop und clear alle fehlerfrei. Man verzichtet damit allerdings auf alle späteren Korrekturen. Nutze dies nur als Übergangslösung.
C. Eine kleine clustersichere Session schreiben. Die Sessions des SDK bilden eine Schnittstelle mit vier Methoden ( get_items, add_items, pop_item, clear_session ). Verwende einen einzigen Listenschlüssel mit einem Redis-Cluster-Hash-Tag ( {...} ), sodass alles für eine Session in einem Slot liegt und keine Transaktion nötig ist:
import json from agents.memory import SessionABC class ClusterRedisSession ( SessionABC ): """ Session history on Redis Cluster: one list key, hash-tagged by session id. """ def __init__ ( self, session_id: str, redis_client, key_prefix: str = " agents " ): self. session_id = session_id self. _redis = redis_client # {...} is a hash tag: every key for this session maps to one slot. self. _key = f " { key_prefix }:{{ { session_id } }}:messages " async def get_items ( self, limit: int | None = None ): start = - limit if limit else 0 raw = await self. _redis. lrange ( self. _key, start, - 1 ) return [ json. loads ( r ) for r in raw ] async def add_items ( self, items ): if items: await self. _redis. rpush ( self. _key, * [ json. dumps ( i ) for i in items ]) async def pop_item ( self ): raw = await self. _redis. rpop ( self. _key ) return json. loads ( raw ) if raw else None async def clear_session ( self ): await self. _redis. delete ( self. _key ) Enter fullscreen mode Exit fullscreen mode Die Verwendung erfolgt wie bei jeder anderen Session:
from redis.asyncio.cluster import RedisCluster from agents import Agent, Runner client = RedisCluster ( host = " your-cluster-host ", port = 6379 ) session = ClusterRedisSession ( " user-123:chat-1 ", client, key_prefix = " agents " ) result = await Runner. run ( agent, " Hi again ", session = session ) Enter fullscreen mode Exit fullscreen mode Was ich auf dem lokalen Cluster mit drei Knoten verifiziert habe: add/get (einschließlich limit=1, das das neueste Element zurückgibt), pop und clear verhielten sich alle wie erwartet. Zwei Runner.run-Durchläufe mit einem Platzhaltermodell speicherten sowohl die Nutzer- als auch die Assistenten-Einträge, und die Modelleingabe des zweiten Durchlaufs enthielt den Verlauf des ersten. repro:{s1}:messages und repro:{s1}:counter landen beide auf Slot 15224. Einen echten LLM-Aufruf habe ich nicht durchgeführt und keine gleichzeitigen Schreibzugriffe getestet.
Abwägungen: RPUSH ist atomar für einen einzelnen Aufruf, aber dieser Ansatz umgeht die WATCH-basierten Schutzmechanismen des SDK und dessen session_settings-Behandlung. Gleichzeitige Schreiber auf dieselbe Session mischen sich in Ankunftsreihenfolge. Die SDK-Dokumentation besagt, dass Drittanbieter-Bibliotheken das Session-Protokoll implementieren sollten, anstatt SessionABC zu erben. Vererbung ist im eigenen Anwendungscode in Ordnung. SessionABC deklariert session_settings mit dem Standardwert None, daher muss diese Klasse es nicht setzen.
Eine Session spielt den Verlauf einer einzelnen Konversation wieder. Sie bietet kein Nutzergedächtnis über verschiedene Sessions hinweg. Das ist eine separate Ebene, sei es über die anderen Memory-Features des SDK oder einen gehosteten Speicher. MemorySync bietet einen OpenAI Agents SDK-Leitfaden für diese sprachübergreifende Ebene über Sessions hinweg. Er ersetzt deinen Redis-Session-Speicher nicht.
Behebt ein Upgrade das Problem? Stand 0.24.0: Nein. Man erhält eine klare TypeError-Meldung statt der AttributeError. Beobachte #5322 für echte Cluster-Unterstützung. Der Melder schlug hash-getaggte Schlüssel plus ein einnodiges WATCH/MULTI vor.
Warum funktionieren get_items und pop_item weiterhin? Laut Issue nutzen nur die transaktionalen Schreibpfade die angehängte Pipeline.
Kann ich einfach das Präfix hash-taggen, das ich an RedisSession übergebe? Das Schlüsselformat lautet {prefix}:{id}.... Ein Tag im Präfix würde alle Sessions in einem Slot ablegen, was die Last auf einen Knoten konzentriert. Es würde auch das fehlende Pipeline-Attribut nicht beheben, da 0.24.0 jeden RedisCluster-Client beim Schreiben ablehnt.
Managed Redis bietet nur den Cluster-Modus an. Ist Option C produktionstauglich? Es ist ein kleiner, gut lesbarer Speicher, aber teste ihn zuerst unter deiner echten Concurrency auf einem Staging-Cluster.
Neugierig, wie querschnittliches Gedächtnis über Sessions hinweg neben dem Session-Verlauf aussieht? Probiere MemorySync im Browser aus. Eine Registrierung ist nicht erforderlich.
Automatisch übersetzt. Das Original auf Englisch ist unten aufklappbar.
Original (Englisch) anzeigen
Posted on Oct 25 • Originally published at memorysync.hashnode.dev
Disclosure: I'm Mohammed, founder of MemorySync. I drafted this with AI assistance from the public GitHub issue and the SDK source. Code status: on Oct 25, 2026 I installed openai-agents 0.24.0 and redis 8.1.0 (Python 3.13). I started a local 3-node Redis Cluster (Redis 8.0.2), confirmed that ClusterPipeline lacks the hooked attributes, reproduced the new TypeError from add_items and clear_session with a RedisCluster client, and ran the custom session below against that cluster, including two Runner.run turns with a stand-in model (no OpenAI key, so no real LLM call). I checked the hash-slot math with redis-py's key_slot.
With the OpenAI Agents SDK's Redis-backed session and a cluster client ( redis.asyncio.cluster.RedisCluster ), storing history fails:
File "agents/extensions/memory/redis_session.py", line 483, in _write_items_attempt raise_first_error = pipe.raise_first_error AttributeError: 'ClusterPipeline' object has no attribute 'raise_first_error' Enter fullscreen mode Exit fullscreen mode This is openai/openai-agents-python#5322, still open as of Oct 25, 2026. The reporter saw it from 0.20.0 through 0.23.1. Per the issue, add_items and (on latest) clear_session fail, get_items and pop_item work, and 0.14.5 worked for all four.
On 0.24.0 you'll get a clearer error instead. I reproduced this against a local Redis Cluster:
TypeError: RedisSession does not support transactions with RedisCluster clients. Use redis.asyncio.Redis connected to a standalone Redis server, or implement a custom Session backend for Redis Cluster. Enter fullscreen mode Exit fullscreen mode In 0.24.0 the check sits in the write path, not in __init__. Constructing the session succeeds, and the first write raises. That comes from PR #5361 ("scope Redis Cluster rejection to transactional writes"), which the issue links and which was merged on Oct 14, 2026. On the cluster, get_items and pop_item still worked on 0.24.0, and add_items and clear_session raised the TypeError.
A. Point RedisSession at a standalone (non-cluster) Redis. This is what the SDK's error message suggests. It's simplest if your provider offers a non-cluster mode.
B. Pin openai-agents==0.14.5. The issue reports that it worked on a cluster, and on my local cluster (with redis 8.1.0) add, get, pop and clear all ran. You'd give up every later fix, though. Use this only as a stopgap.
C. Write a small cluster-safe Session. The SDK's sessions are a four-method interface ( get_items, add_items, pop_item, clear_session ). Use one list key with a Redis Cluster hash tag ( {...} ), so everything for a session lives in one slot and needs no transaction:
import json from agents.memory import SessionABC class ClusterRedisSession ( SessionABC ): """ Session history on Redis Cluster: one list key, hash-tagged by session id. """ def __init__ ( self, session_id: str, redis_client, key_prefix: str = " agents " ): self. session_id = session_id self. _redis = redis_client # {...} is a hash tag: every key for this session maps to one slot. self. _key = f " { key_prefix }:{{ { session_id } }}:messages " async def get_items ( self, limit: int | None = None ): start = - limit if limit else 0 raw = await self. _redis. lrange ( self. _key, start, - 1 ) return [ json. loads ( r ) for r in raw ] async def add_items ( self, items ): if items: await self. _redis. rpush ( self. _key, * [ json. dumps ( i ) for i in items ]) async def pop_item ( self ): raw = await self. _redis. rpop ( self. _key ) return json. loads ( raw ) if raw else None async def clear_session ( self ): await self. _redis. delete ( self. _key ) Enter fullscreen mode Exit fullscreen mode Use it like any other session:
from redis.asyncio.cluster import RedisCluster from agents import Agent, Runner client = RedisCluster ( host = " your-cluster-host ", port = 6379 ) session = ClusterRedisSession ( " user-123:chat-1 ", client, key_prefix = " agents " ) result = await Runner. run ( agent, " Hi again ", session = session ) Enter fullscreen mode Exit fullscreen mode What I verified on the local 3-node cluster: add/get (including limit=1 returning the newest item), pop and clear all behaved as expected. Two Runner.run turns with a stand-in model stored both user and assistant items, and the second turn's model input included the first turn's history. repro:{s1}:messages and repro:{s1}:counter both hash to slot 15224. I didn't make a real LLM call or test with concurrent writers.
Trade-offs: RPUSH is atomic for a single call, but this skips the SDK's WATCH-based protections and its session_settings handling. Concurrent writers to the same session interleave in arrival order. The SDK docstring says third-party libraries should implement the Session protocol rather than subclass SessionABC. Subclassing is fine in your own app code. SessionABC declares session_settings with a default of None, so this class doesn't need to set it.
A session replays one conversation's history. It doesn't give a user memory across different sessions. That's a separate layer, whether you use the SDK's other memory features or a hosted store. MemorySync has an OpenAI Agents SDK guide for that cross-session layer. It doesn't replace your Redis session store.
Will upgrading fix it? As of 0.24.0, no. You get a clear TypeError instead of the AttributeError. Watch #5322 for real cluster support. The reporter proposed hash-tagged keys plus a single-node WATCH/MULTI.
Why do get_items and pop_item still work? According to the issue, only the transactional write paths use the hooked pipeline.
Can I just hash-tag the prefix I pass to RedisSession? The key layout is {prefix}:{id}.... A tag in the prefix would put all sessions in one slot, which concentrates load on one node. It also wouldn't fix the missing pipeline attributes, because 0.24.0 rejects any RedisCluster client on write.
Managed Redis only offers cluster mode. Is option C safe for production? It's a small, readable store, but test it under your real concurrency on a staging cluster first.
Curious what cross-session memory looks like next to session history? Try MemorySync in the browser. No signup is needed.