HKUDS/Vibe-Trading · error · ValueError

memory_type must be one of

Error message

memory_type must be one of: {', '.join(MEMORY_TYPES)}

What it means

PersistentMemory.add only accepts memory_type values from the module-level MEMORY_TYPES whitelist; anything else is rejected before the record is stored. The allowed values are enumerated in the error message itself.

Solutions

  1. Use one of the types listed in the error message (import MEMORY_TYPES to reference them)
  2. Validate user/LLM input against MEMORY_TYPES before calling add
  3. If a new type is genuinely needed, extend MEMORY_TYPES in agent/src/memory/persistent.py

Example fix

# before
mem.add(name=..., memory_type="thought", content=...)
# after
from agent.src.memory.persistent import MEMORY_TYPES
assert memory_type in MEMORY_TYPES
mem.add(name=..., memory_type=memory_type, content=...)
Defensive patterns

Strategy: type-guard

Validate before calling

from agent.src.memory.persistent import MEMORY_TYPES
memory_type = (memory_type or '').strip().lower()
if memory_type not in MEMORY_TYPES:
    memory_type = 'observation'  # or reject explicitly
mem.add(name=name, memory_type=memory_type, content=content)

Type guard

def is_valid_memory_type(t) -> bool:
    return isinstance(t, str) and t.strip().lower() in MEMORY_TYPES

Try / catch

try:
    mem.add(name=name, memory_type=memory_type, content=content)
except ValueError as exc:
    if 'memory_type must be one of' in str(exc):
        mem.add(name=name, memory_type='observation', content=content)

Prevention

When it happens

Trigger: Calling add(..., memory_type="note") when the allowed set is e.g. observation/decision/outcome; passing a user-supplied or LLM-generated type string without validating it against MEMORY_TYPES.

Common situations: Prompt/tool schemas that allow free-form type fields, version drift where MEMORY_TYPES changed between releases, or casing mismatches like 'Observation'.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


AI-assisted analysis of HKUDS/Vibe-Trading@80ffdda44c (2026-08-28). Data as JSON: /api/errors/47813175b0d01f9f. Report an issue: GitHub.

Appendix: source

Thrown at agent/src/memory/persistent.py:499

        name: str,
        content: str,
        memory_type: str = "project",
        description: str = "",
    ) -> Optional[Path]:
        """Save a new memory entry and update the index."""
        if _is_quality_enabled() and self.is_duplicate(name, description, content):
            logger.debug(
                "Duplicate memory write blocked within %.0fs window: %s",
                DEDUP_WINDOW_SECONDS,
                name,
            )
            return None

        stripped_name = name.strip()
        if not stripped_name:
            raise ValueError("memory name must not be empty or whitespace-only")
        if memory_type not in MEMORY_TYPES:
            raise ValueError(f"memory_type must be one of: {', '.join(MEMORY_TYPES)}")

        slug = _SLUG_DISALLOWED_RE.sub("_", stripped_name.lower())[:60]
        if slug.strip("_") == "":
            digest = hashlib.sha256(stripped_name.encode("utf-8")).hexdigest()[:6]
            slug = f"{slug}_{digest}" if slug else digest

        from src.config.accessor import get_env_config
        if get_env_config().memory.hierarchy_enabled:
            from src.memory.hierarchy import MemoryHierarchy
            hierarchy = MemoryHierarchy(self._dir)
            # route_entry() treats its second argument as the leaf filename
            # verbatim, so the ".md" has to be here: a bare slug wrote entries
            # with no suffix, and every scan filters on suffix == ".md", which
            # made them invisible to list_entries() and find(). The category
            # directory already carries the type, and the name must match what
            # recover_extensionless_entries() renames orphans to, or the same
            # entry ends up on disk twice.
            path = hierarchy.route_entry(memory_type, f"{slug}.md")

View on GitHub (pinned to 80ffdda44c)