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

  1. Delete or restore {storage_path}/openviking/sessions/{session_id}.json — a missing file yields {} and the session resumes with a fresh cursor
  2. Do not write external tooling output into the openviking/sessions directory; it is private backend state
  3. 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

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


AI-assisted analysis of bytedance/deer-flow@1dd6ba1acb (2026-08-14). Data as JSON: /api/errors/1c3acc1818435629. Report an issue: GitHub.