iflytek/astron-agent · warning · CustomException

23902

23902

Error message

Conversation does not exist or is not locked

What it means

EventRegistry.unlock_event (core/workflow/cache/event_registry.py:131) raises EVENT_REGISTRY_NOT_LOCK_ERROR when check_event_lock(event_id) is false — i.e. no event_lock:{event_id} key exists in the cache — so there is nothing to unlock. Unlocking is only valid for an event that currently holds a lock (code 23902).

Solutions

  1. Only call unlock_event when you know your code path acquired the lock (track ownership with a boolean).
  2. Check EventRegistry().check_event_lock(event_id) before unlocking, mirroring the guard inside the method.
  3. Treat 'not locked' as success in cleanup paths — the goal (no lock) is already achieved.
  4. If locks are persistently missing/expiring early, review the cache TTL configuration and clock/TTL behavior of the cache service.

Example fix

# before
finally:
    EventRegistry().unlock_event(event_id=event_id)  # raises if never locked

# after
locked = False
try:
    EventRegistry().lock_event(event_id=event_id, sid=sid)
    locked = True
    ...
finally:
    if locked and EventRegistry().check_event_lock(event_id=event_id):
        EventRegistry().unlock_event(event_id=event_id)
Defensive patterns

Strategy: try-catch

Validate before calling

if EventRegistry().check_event_lock(event_id=event_id):
    EventRegistry().unlock_event(event_id=event_id)

Type guard

def is_locked(event_id: str) -> bool:
    return bool(EventRegistry().check_event_lock(event_id=event_id))

Try / catch

try:
    EventRegistry().unlock_event(event_id=event_id)
except CustomException as e:
    if e.code == CodeEnum.EVENT_REGISTRY_NOT_LOCK_ERROR.code:
        pass  # already unlocked — treat as success in cleanup
    else:
        raise

Prevention

When it happens

Trigger: Calling unlock_event for an event that was never locked, whose lock already expired (TTL) or was deleted by a prior unlock; race where two cleanup paths unlock concurrently and the second finds no key.

Common situations: Error-handling code unconditionally calls unlock_event even when the resume path never reached lock_event; lock TTL elapsed during a long-running resume and a finally-block tries to unlock; duplicate unlock calls from nested exception handlers.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/61fb44bf48f2cf22. Report an issue: GitHub.

Appendix: source

Thrown at core/workflow/cache/event_registry.py:131

        :raise Exception: Raises exception if saving event fails
        """
        try:
            cls.save_event(event)
        except Exception as e:
            raise e

    @classmethod
    def lock_event(cls, event_id: str, sid: str, timeout: int = 180) -> None:
        get_cache_service().set_ex(
            key=f"event_lock:{event_id}",
            value=f"locked_by_{sid}",
            expire_time=timeout,
        )

    @classmethod
    def unlock_event(cls, event_id: str) -> None:
        if not cls.check_event_lock(event_id=event_id):
            raise CustomException(err_code=CodeEnum.EVENT_REGISTRY_NOT_LOCK_ERROR)
        get_cache_service().delete(key=f"event_lock:{event_id}")

    @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
        """

View on GitHub (pinned to 5e758547a8)