From 3f97fcec6ce3af127e4bb6cb3844e01ee0c8a9c1 Mon Sep 17 00:00:00 2001 From: icn5381 <255778606+icn5381@users.noreply.github.com> Date: Wed, 12 Aug 2026 18:18:31 +0800 Subject: [PATCH] fix(cli): surface memory backend start failures on stderr The four backend.start() call sites (agent REPL x2, gateway, TUI) caught the failure with logger.exception only. Under the TUI loguru is redirected to a file, so an invalid memory identity (rejected by EverosBackend in start()) silently turned long-term memory off with nothing on screen. Add a warn_memory_start_failed helper that prints to stderr (mirroring the embedding-unavailable warning in everos/backend.py) and call it from every start() call site, so a degraded-memory condition is visible in every entry point instead of only the agent REPL and gateway. Co-authored-by: Claude (claude-opus-5) --- raven/cli/_memory_warn.py | 21 +++++++++++++++++++++ raven/cli/agent_commands.py | 10 ++++++++-- raven/cli/gateway_commands.py | 5 ++++- raven/cli/tui_commands.py | 5 ++++- tests/test_memory_warn.py | 19 +++++++++++++++++++ 5 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 raven/cli/_memory_warn.py create mode 100644 tests/test_memory_warn.py diff --git a/raven/cli/_memory_warn.py b/raven/cli/_memory_warn.py new file mode 100644 index 00000000..fa4cbd2e --- /dev/null +++ b/raven/cli/_memory_warn.py @@ -0,0 +1,21 @@ +"""User-visible warning when the memory backend fails to start. + +Under the TUI, loguru is redirected to a file, so a start failure (e.g. a +memory identity that EverOS rejects) silently disables long-term memory with +nothing on screen. Mirror the embedding-unavailable warning in +``everos/backend.py`` and print to stderr so every entry point (TUI, agent +REPL, gateway) sees the same degraded-memory notice, not just the two that +already surface the traceback via stderr. +""" + +from __future__ import annotations + +from rich.console import Console + + +def warn_memory_start_failed(exc: BaseException) -> None: + """Print a stderr notice that long-term memory is off this session.""" + Console(stderr=True).print( + "[yellow]Memory backend failed to start; long-term memory is off this session.[/yellow]\n" + f"[dim]Run `raven onboard` to reconfigure, or check the log ({type(exc).__name__}).[/dim]" + ) diff --git a/raven/cli/agent_commands.py b/raven/cli/agent_commands.py index 715c64f4..6e343002 100644 --- a/raven/cli/agent_commands.py +++ b/raven/cli/agent_commands.py @@ -398,10 +398,13 @@ async def run_once(): if backend is not None: try: await backend.start() - except Exception: + except Exception as exc: logger.exception( "memory backend start failed; continuing with legacy memory path", ) + from raven.cli._memory_warn import warn_memory_start_failed + + warn_memory_start_failed(exc) try: # Build inside the running loop: Scheduler pins its home loop in # __init__, so build_repl must not run in the sync prologue. @@ -496,10 +499,13 @@ async def run_interactive(): if backend is not None: try: await backend.start() - except Exception: + except Exception as exc: logger.exception( "memory backend start failed; continuing with legacy memory path", ) + from raven.cli._memory_warn import warn_memory_start_failed + + warn_memory_start_failed(exc) # agent_loop.run() is now a lifecycle keep-alive (executor / # debug server / MCP up, then idle); all turns go through the # spine. Gathered on teardown. diff --git a/raven/cli/gateway_commands.py b/raven/cli/gateway_commands.py index 9edfccca..ce79351e 100644 --- a/raven/cli/gateway_commands.py +++ b/raven/cli/gateway_commands.py @@ -368,10 +368,13 @@ async def run(): if backend is not None: try: await backend.start() - except Exception: + except Exception as exc: _logger.exception( "memory backend start failed; continuing with legacy memory path", ) + from raven.cli._memory_warn import warn_memory_start_failed + + warn_memory_start_failed(exc) try: # Spine assembly for the gateway's host sources (cron submits # through it, replies route to channels via a per-channel outlet). diff --git a/raven/cli/tui_commands.py b/raven/cli/tui_commands.py index 3c53367e..5cf994d4 100644 --- a/raven/cli/tui_commands.py +++ b/raven/cli/tui_commands.py @@ -706,12 +706,15 @@ def _agent_loop_factory(): async def _start_backend() -> None: try: await agent_loop.backend.start() # type: ignore[union-attr] - except Exception: + except Exception as exc: from loguru import logger as _logger _logger.exception( "tui: memory backend start failed; continuing with degraded memory path", ) + from raven.cli._memory_warn import warn_memory_start_failed + + warn_memory_start_failed(exc) _strip_tty_stream_handlers() asyncio.create_task(_start_backend()) diff --git a/tests/test_memory_warn.py b/tests/test_memory_warn.py new file mode 100644 index 00000000..0d1da6d8 --- /dev/null +++ b/tests/test_memory_warn.py @@ -0,0 +1,19 @@ +"""warn_memory_start_failed must surface on stderr (visible under the TUI).""" + +from __future__ import annotations + +import pytest + +from raven.cli._memory_warn import warn_memory_start_failed + + +def test_warn_memory_start_failed_writes_degraded_notice_to_stderr( + capsys: pytest.CaptureFixture[str], +) -> None: + warn_memory_start_failed(ValueError("memory.userId='..' is not accepted")) + + captured = capsys.readouterr() + combined = captured.err + captured.out + + assert "long-term memory is off" in combined + assert "ValueError" in combined