langchain-ai/deepagents · error · RuntimeError
UnixSocketEventSource is already started
Error message
UnixSocketEventSource is already started
What it means
`UnixSocketEventSource.start()` binds and serves on a Unix socket; calling it twice would attempt to rebind a socket the instance already owns. The method raises this RuntimeError when the internal server handle (`self._server`) is already set, enforcing a strict start-once lifecycle. `stop()` is the idempotent counterpart for cleanup.
Source
Thrown at libs/code/deepagents_code/event_bus.py:157
self,
sink: Callable[[ExternalEvent], Awaitable[None]],
) -> None:
"""Start listening for newline-delimited JSON events.
Args:
sink: Async callback invoked with each decoded event.
Raises:
RuntimeError: If `start()` has already been called on this
instance without a subsequent `stop()`.
FileExistsError: If the socket path is occupied by a non-socket
filesystem entry.
OSError: If the socket cannot be bound (e.g. permission denied,
path too long).
""" # noqa: DOC502 # FileExistsError/OSError propagate from helpers
if self._server is not None:
msg = "UnixSocketEventSource is already started"
raise RuntimeError(msg)
self._sink = sink
self.path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
with contextlib.suppress(FileNotFoundError):
_unlink_existing_socket(self.path)
previous_umask = os.umask(0o077)
try:
self._server = await asyncio.start_unix_server(
self._handle_client,
path=str(self.path),
limit=_MAX_LINE_BYTES,
)
finally:
os.umask(previous_umask)
# Defense in depth: even if `start_unix_server` somehow ignored the
# umask (different libc, mocked socket layer), force-tighten the mode.View on GitHub (pinned to a1af029e6e)
Solutions
- Track a started flag yourself and skip `start()` when it is already set.
- Call `await source.stop()` before a deliberate restart, then `start()` again.
- Create a fresh `UnixSocketEventSource` instance for each start cycle instead of restarting the old one.
- Move startup to a single place in the app lifecycle so it runs exactly once.
Example fix
// before
async def reconnect(src):
await src.start() # RuntimeError if already started
// after
async def reconnect(src):
await src.stop()
await src.start() Defensive patterns
Strategy: try-catch
Validate before calling
if getattr(source, "_server", None) is not None:
return # already started
await source.start() Type guard
def is_started(source) -> bool:
return getattr(source, "_server", None) is not None Try / catch
try:
await source.start()
except RuntimeError as exc:
if "already started" in str(exc):
logger.debug("Event source already running; skipping start")
else:
raise Prevention
- Track your own started flag around the source lifecycle.
- Route all start/restart logic through one idempotent helper that stops before starting.
- Avoid starting the same source from multiple app hooks.
When it happens
Trigger: Calling `start()` a second time on the same instance — e.g. a reconnect routine that reuses the object, or an init path racing a background starter without checking state.
Common situations: Retry logic that restarts the same source instead of creating a new one; app reload hooks firing initialization twice; wiring that starts the source in both a screen handler and an app-level hook.
Related errors
- UnixSocketEventSource.serve_forever called before start()
- Refusing to remove non-socket external event path: {path}
- Server process is not running
- A workspace is required to start the remote agent.
- Session start stopped by hook
AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29).
Data as JSON: /api/errors/a3fdcf161405bf18.
Report an issue: GitHub.