iflytek/astron-agent · error · CustomException
23900
23900
Error message
Conversation has timed out or does not exist
What it means
Raised by EventRegistry.get_event when no entry exists in Redis for the given event_id key. The workflow uses this registry to park events awaiting user (interrupt) responses; a missing entry means the event expired (TTL) or was deleted. Callers like update_event, on_interrupt, on_interrupt_node_start/end surface it as 'Conversation has timed out or does not exist'.
Solutions
- Check event existence with get_cache_service().get(key=EventRegistry._event_key(event_id)) before resuming, and return a friendly 'conversation expired' response to the user
- Extend or configure the TTL used when the event is stored in the registry so long-running waits are not evicted
- Regenerate the conversation: have the client start a new workflow run instead of resuming a dead event_id
- Verify Redis persistence/eviction settings (maxmemory-policy) so events are not dropped unexpectedly
Example fix
// before
result = await agent.resume(event_id, user_input) # raises 23900
// after
if not cache.get(key=EventRegistry._event_key(event_id)):
return ConversationExpiredResponse(event_id=event_id)
result = await agent.resume(event_id, user_input) Defensive patterns
Strategy: try-catch
Validate before calling
def event_exists(event_id: str) -> bool:
return get_cache_service().get(key=EventRegistry._event_key(event_id)) is not None Try / catch
try:
event = EventRegistry.get_event(event_id)
except CustomException as e:
if e.err_code == CodeEnum.EVENT_REGISTRY_NOT_FOUND_ERROR:
return expire_conversation(event_id)
raise Prevention
- Check key existence before resuming an interrupted conversation
- Set realistic TTLs for parked events
- Handle Redis restarts/flushes as conversation loss and prompt users to restart
- Deduplicate resume submissions so del_event is not raced
When it happens
Trigger: Calling EventRegistry.get_event(event_id) (directly or via update_event/on_interrupt* handlers) with an event_id whose Redis key has expired via TTL or was removed by del_event; resuming a conversation after the wait window elapsed; passing a malformed/never-created event_id.
Common situations: User replies to an interrupted workflow node after the session TTL expired; stale client resuming an old conversation ID after a Redis restart or cache flush; double-submit of a resume request where the first one deleted the event.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/7e3adef90c22e9b7.
Report an issue: GitHub.
Appendix: source
Thrown at core/workflow/cache/event_registry.py:152
@classmethod
def check_event_lock(cls, event_id: str) -> bool:
data = get_cache_service().get(key=f"event_lock:{event_id}")
if not data:
return False
return True
@classmethod
def get_event(cls, event_id: str) -> Event:
"""
Get event information by event ID.
:param cls: Class itself
:param event_id: Event ID string
:return: Decoded event object if found, raises exception otherwise
"""
data = get_cache_service().get(key=cls._event_key(event_id))
if not data:
raise CustomException(err_code=CodeEnum.EVENT_REGISTRY_NOT_FOUND_ERROR)
return cls._decode(data)
@classmethod
def del_event(cls, event_id: str) -> None:
"""
Delete event by event ID.
:param cls: Class itself for accessing class variables and methods
:param event_id: ID of the event to delete
"""
get_cache_service().delete(cls._event_key(event_id))
@classmethod
def get_all_event_ids(cls) -> dict:
"""
Get all event IDs from cache.
:return: Dictionary containing all event IDsView on GitHub (pinned to 5e758547a8)