bytedance/deer-flow · error · MemoryManagerError
OpenViking capture cursor is invalid; refusing unsafe replay
Error message
OpenViking capture cursor is invalid; refusing unsafe replay (session={session_id}) What it means
_load_cursor() raises MemoryManagerError when the per-session cursor file at {storage_path}/openviking/sessions/{session_id}.json parses as valid JSON but is not an object (e.g. a JSON list, string, or number). The cursor must be a dict holding the suffix position and seen-message-ID set; any other top-level type means the state format is wrong, and replaying from it is unsafe, so the backend refuses.
Source
Thrown at backend/packages/harness/deerflow/agents/memory/backends/openviking/openviking_manager.py:509
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:
logger.debug(
"Failed to remove OpenViking cursor temp file: %s",View on GitHub (pinned to 1dd6ba1acb)
Solutions
- Delete or restore {storage_path}/openviking/sessions/{session_id}.json — a missing file yields {} and the session resumes with a fresh cursor
- Do not write external tooling output into the openviking/sessions directory; it is private backend state
- With write_failure_policy: log_and_drop (default) the failure is logged and the conversation continues; with 'raise' it becomes MemoryManagerError
Defensive patterns
Strategy: try-catch
Validate before calling
import json, pathlib
def cursor_well_formed(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:
return isinstance(json.loads(p.read_text(encoding="utf-8")), dict)
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 invalid" in str(exc):
reset_cursor_file(session_id) # remove non-dict JSON so the next write starts fresh
else:
raise Prevention
- Never let external tooling write into {storage_path}/openviking/sessions/
- Treat the sessions directory as private backend state owned by the OpenViking adapter
When it happens
Trigger: Someone or some tool replaced the cursor file with e.g. '[1,2,3]' or '"done"' — valid JSON, wrong shape. Distinct from error 630, which covers unreadable/unparseable content; this fires after successful json.loads when isinstance(value, dict) is False.
Common situations: Hand-editing session files; a downgrade/upgraded schema mismatch where an external tool wrote a different format into the same path.
Related errors
- OpenViking capture cursor is unreadable; refusing unsafe rep
- OpenViking health check returned an unhealthy response
- OpenViking memory write requires thread_id
- OpenViking USER API key is bound to DeerFlow owner_user_id {
- {message} (session={session_id})
AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14).
Data as JSON: /api/errors/1c3acc1818435629.
Report an issue: GitHub.