bytedance/deer-flow · error · MemoryManagerError
OpenViking capture cursor is unreadable; refusing unsafe rep
Error message
OpenViking capture cursor is unreadable; refusing unsafe replay (session={session_id}) What it means
_load_cursor() raises MemoryManagerError when the per-session capture cursor file ({storage_path}/openviking/sessions/{session_id}.json) exists but cannot be read or parsed (OSError from reading, ValueError from json.loads). The cursor tracks the full-transcript suffix position and seen message IDs; rather than risk replaying already-captured messages into OpenViking (duplicates), the backend refuses to continue for that session.
Source
Thrown at backend/packages/harness/deerflow/agents/memory/backends/openviking/openviking_manager.py:507
def _close_resources(self) -> None:
with self._resource_lock:
if self._resources_closed:
return
self._recorder.close()
self._resources_closed = True
def _state_path(self, session_id: str) -> Path:
root = Path(self._config.storage_path or ".") / "openviking" / "sessions"
return root / f"{session_id}.json"
def _load_cursor(self, session_id: str) -> dict[str, Any]:
path = self._state_path(session_id)
try:
value = json.loads(path.read_text(encoding="utf-8"))
except FileNotFoundError:
return {}
except (OSError, ValueError) as exc:
raise MemoryManagerError(f"OpenViking capture cursor is unreadable; refusing unsafe replay (session={session_id})") from exc
if not isinstance(value, dict):
raise MemoryManagerError(f"OpenViking capture cursor is invalid; refusing unsafe replay (session={session_id})")
return value
def _save_cursor(self, session_id: str, state: dict[str, Any]) -> None:
path = self._state_path(session_id)
path.parent.mkdir(parents=True, exist_ok=True)
temp_path = path.with_suffix(f".{os.getpid()}.{threading.get_ident()}.tmp")
try:
temp_path.write_text(
json.dumps(state, ensure_ascii=False, indent=2),
encoding="utf-8",
)
os.replace(temp_path, path)
finally:
try:
temp_path.unlink(missing_ok=True)
except OSError:View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Inspect {storage_path}/openviking/sessions/{session_id}.json — if it is truncated/garbage, delete it: an absent file is treated as a fresh cursor (FileNotFoundError returns {}), trading a one-time re-capture risk for recovery
- Fix filesystem permissions on storage_path so the Gateway process can read/write the sessions directory
- If write_failure_policy is 'raise', expect this to surface as MemoryManagerError on the write; with 'log_and_drop' (default) it is logged and the turn continues
Example fix
# before: corrupted cursor blocks the session
# rm .deer-flow/openviking/sessions/<session_id>.json
# after (recovery)
import pathlib
pathlib.Path(storage_path, 'openviking', 'sessions', f'{session_id}.json').unlink(missing_ok=True) # next write starts a fresh cursor Defensive patterns
Strategy: try-catch
Validate before calling
import json, pathlib
def cursor_readable(session_id: str, storage_path: str) -> bool:
p = pathlib.Path(storage_path or ".", "openviking", "sessions", f"{session_id}.json")
if not p.exists():
return True
try:
json.loads(p.read_text(encoding="utf-8"))
return True
except (OSError, ValueError):
return False Try / catch
from deerflow.agents.memory.manager import MemoryManagerError
try:
manager.add(thread_id, messages, user_id=user)
except MemoryManagerError as exc:
if "cursor is unreadable" in str(exc):
recover_cursor(session_id) # delete the corrupt file to reset the cursor
else:
raise Prevention
- Keep storage_path on reliable local disk; avoid hand-editing openviking/sessions/*.json
- Snapshots of storage_path are safe: _save_cursor writes temp + os.replace atomically
- Monitor for ERROR logs mentioning 'capture cursor' to catch corruption early
When it happens
Trigger: The cursor JSON file is corrupted (truncated by a crash mid-write — although _save_cursor uses a temp file + os.replace), has wrong permissions, sits on an unreadable mount, or contains invalid JSON. Raises on the next write for that session, after _load_cursor is called inside the session lock.
Common situations: Disk-full or hard-kill events corrupting files despite atomic replace; operators hand-editing the sessions file; storage_path on NFS with permission changes between runs.
Related errors
- OpenViking capture cursor is invalid; refusing unsafe replay
- Failed to clear memory data.
- Failed to create memory fact.
- Failed to delete memory fact.
- Failed to update memory fact.
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/4d27a382dac79644.
Report an issue: GitHub.