infiniflow/ragflow · error · NotFoundException

NOT_FOUND

NOT_FOUND

Error message

Memory '{memory_id}' not found.

What it means

Raised by _require_memory_access when MemoryService.get_by_memory_id returns no row, or when the row exists but the caller is not allowed to see it (not the owner, and either the memory is not TEAM-permission or the caller has not joined that tenant). Deliberately a single NotFound error for both 'missing' and 'forbidden' to avoid leaking existence of other tenants' memories.

Source

Thrown at api/apps/services/memory_api_service.py:67


def _joined_tenant_ids(user_id: str) -> set[str]:
    user_tenants = UserTenantService.get_user_tenant_relation_by_user_id(user_id)
    return {user_id, *[tenant["tenant_id"] for tenant in user_tenants]}


def _memory_accessible(memory) -> bool:
    if memory.tenant_id == current_user.id:
        return True
    if memory.permissions != TenantPermission.TEAM.value:
        return False
    return memory.tenant_id in _joined_tenant_ids(current_user.id)


def _require_memory_access(memory_id: str):
    memory = MemoryService.get_by_memory_id(memory_id)
    if not memory or not _memory_accessible(memory):
        raise NotFoundException(f"Memory '{memory_id}' not found.")
    return memory


def _filter_accessible_memories(memory_ids: list[str]):
    memory_ids = _split_filter_values(memory_ids)
    if not memory_ids:
        return []
    return [memory for memory in MemoryService.get_by_ids(memory_ids) if _memory_accessible(memory)]


async def create_memory(memory_info: dict):
    """
    :param memory_info: {
        "name": str,
        "memory_type": list[str],
        "embd_id": str,
        "llm_id": str,
        "tenant_embd_id": str | None,

View on GitHub (pinned to 554fb1133a)

Solutions

  1. Re-list memories (GET memory collection) to get fresh, accessible ids and drop cached ones
  2. If the memory belongs to another user, ask the owner to set permissions=TEAM and have the caller join that tenant
  3. Verify the id is the exact uuid returned by the create/list API
  4. Handle 404 as terminal — do not retry; refresh the id source instead
Defensive patterns

Strategy: validation

Validate before calling

import uuid

def valid_memory_id(mid: str) -> bool:
    try:
        uuid.UUID(mid)
        return True
    except (ValueError, TypeError):
        return False
# also: re-list accessible memories and only use returned ids

Type guard

def is_memory_id(v) -> bool:
    return isinstance(v, str) and valid_memory_id(v)

Try / catch

try:
    get_memory(mid)
except NotFoundException:
    memories = list_memories()   # refresh cache of ids
    if mid not in {m["id"] for m in memories}:
        drop_cached(mid)         # terminal: forget this id

Prevention

When it happens

Trigger: GET/PUT/DELETE /api/v1/memory/{memory_id} (or memory-list endpoints filtering by ids) with a wrong/revoked uuid, a memory belonging to another user without TEAM permission, or a team memory from a tenant the caller never joined.

Common situations: Stale memory ids kept client-side after deletion; sharing a memory id across accounts without granting team permission; environment resets (DB wiped) while clients hold old ids; copy-paste typos in ids.

Related errors


AI-assisted analysis of infiniflow/ragflow@554fb1133a (2026-08-15). Data as JSON: /api/errors/afba1efdf7426849. Report an issue: GitHub.