{"record":{"id":"acc7a7ff000cc1ff","repo":"bytedance/deer-flow","slug":"openviking-user-api-key-is-bound-to-deerflow-owner","errorCode":null,"errorMessage":"OpenViking USER API key is bound to DeerFlow owner_user_id {self._config.owner_user_id!r}, but this request belongs to {resolved_user!r}. Refusing to share one credential across users.","messagePattern":"OpenViking USER API key is bound to DeerFlow owner_user_id (.+?), but this request belongs to (.+?)\\. Refusing to share one credential across users\\.","errorType":"exception","errorClass":"MemoryManagerError","httpStatus":null,"severity":"critical","filePath":"backend/packages/harness/deerflow/agents/memory/backends/openviking/openviking_manager.py","lineNumber":453,"sourceCode":"        self._save_cursor(\n            session_id,\n            _advanced_cursor(\n                state,\n                signatures,\n                pending_signatures,\n                max_seen=self._config.max_seen_message_ids,\n                commit_pending=False,\n            ),\n        )\n\n    def _resolve_scope(\n        self,\n        user_id: str | None,\n        agent_name: str | None,\n    ) -> str:\n        resolved_user = str(user_id or \"default\")\n        if resolved_user != self._config.owner_user_id:\n            raise MemoryManagerError(f\"OpenViking USER API key is bound to DeerFlow owner_user_id {self._config.owner_user_id!r}, but this request belongs to {resolved_user!r}. Refusing to share one credential across users.\")\n        return _canonical_peer_id(agent_name, self._config.default_peer_id)\n\n    def _actor_peer_scope(\n        self,\n        peer_id: str,\n    ) -> AbstractContextManager[None]:\n        return self._use_actor_peer(peer_id)\n\n    def _session_lock(self, session_id: str) -> threading.RLock:\n        with self._session_locks_guard:\n            return self._session_locks.setdefault(\n                session_id,\n                threading.RLock(),\n            )\n\n    def _begin_operation(self) -> bool:\n        with self._lifecycle:\n            if self._closed:","sourceCodeStart":435,"sourceCodeEnd":471,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/packages/harness/deerflow/agents/memory/backends/openviking/openviking_manager.py#L435-L471","documentation":"OpenVikingMemoryManager._resolve_scope() raises MemoryManagerError when the resolved request user (user_id or 'default') differs from the configured owner_user_id. The backend is credential-bound: one OpenViking USER API key belongs to exactly one DeerFlow user, and the check deliberately fails closed to prevent one user's memory writes/reads being attributed to another under a shared credential.","triggerScenarios":"memory.backend_config.owner_user_id is set to 'alice' but a request arrives with user_id='bob' (or no user_id, resolving to 'default' while owner_user_id is something else). Fires on both the write path (add) and read path (get injected context) of every OpenViking operation.","commonSituations":"Running multi-user/multi-tenant DeerFlow against the single-user OpenViking backend; enabling auth after having configured owner_user_id; IM channel traffic carrying a different owner header than the configured owner_user_id.","solutions":["Align memory.backend_config.owner_user_id with the DeerFlow user actually issuing requests (in no-auth mode the effective user is 'default')","If multiple DeerFlow users must use OpenViking, give each user their own deployment/config with their own USER API key — this backend is single-user by design","Restart the Gateway after changing owner_user_id"],"exampleFix":"# before (config.yaml): owner mismatch\nmemory:\n  backend_config:\n    owner_user_id: alice   # but the deployment runs without auth -> user resolves to 'default'\n\n# after\nmemory:\n  backend_config:\n    owner_user_id: default","handlingStrategy":"validation","validationCode":"owner = str(backend_config.get(\"owner_user_id\", \"\")).strip()\nresolved = str(user_id or \"default\")\nif resolved != owner:\n    raise PermissionError(f\"request user {resolved!r} does not match OpenViking owner {owner!r}\")","typeGuard":"def user_matches_owner(user_id: str | None, owner_user_id: str) -> bool:\n    return str(user_id or \"default\") == owner_user_id","tryCatchPattern":"from deerflow.agents.memory.manager import MemoryManagerError\ntry:\n    manager.add(thread_id, messages, user_id=user_id)\nexcept MemoryManagerError as exc:\n    if \"Refusing to share one credential\" in str(exc):\n        reject_request_with_403()  # credential/user mismatch, do not retry\n    raise","preventionTips":["Configure owner_user_id to the exact effective DeerFlow user ('default' in no-auth mode)","Treat this error as a security boundary: never try to bypass it by rewriting user_id","One OpenViking USER API key per deployment per user; multi-user needs separate configs"],"tags":["openviking","auth","multi-user","memory"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}