HKUDS/DeepTutor · error · ValueError

preferences.md is not auto-consolidated

Error message

preferences.md is not auto-consolidated

What it means

Raised by the L3 update consolidator when it is asked to consolidate the 'preferences' slot. The memory system deliberately excludes preferences.md from automatic consolidation because preferences are user-authored and must not be rewritten/merged by the pipeline. Any code path that routes slot=='preferences' into _run_update_l3 gets this hard rejection.

Source

Thrown at deeptutor/services/memory/consolidator/modes/update.py:374

        new_entry_ids=new_entry_ids,
    )


# ── L3 ──────────────────────────────────────────────────────────────────


async def _run_update_l3(
    slot: L3Slot,
    *,
    language: str,
    user_label: str,
    budget: int,
    llm_selection: dict | None,
    on_event: OnEvent | None,
    settings,
) -> UpdateResult:
    if slot == "preferences":
        raise ValueError("preferences.md is not auto-consolidated")

    meta = load_l3_meta(slot)
    l2_docs = _load_all_l2_docs()
    entries_by_surface: dict[str, list[Entry]] = {}
    seen_now: dict[str, set[str]] = {}
    for surface, doc in l2_docs.items():
        all_entries = doc.all_entries()
        seen_now[surface] = {e.id for e in all_entries}
        # Sort by id (ULID) ascending → roughly time-ascending.
        new_entries = sorted(
            (e for e in all_entries if e.id not in meta.seen_l2_entry_ids.get(surface, set())),
            key=lambda e: e.id,
        )
        entries_by_surface[surface] = new_entries

    new_count = sum(len(v) for v in entries_by_surface.values())
    total_count = sum(len(d.all_entries()) for d in l2_docs.values())
    await emit(

View on GitHub (pinned to 3e82f13042)

Solutions

  1. Filter out the 'preferences' slot before invoking run_update; only auto-consolidatable slots (e.g. identity/life/facts per your slot registry) should be passed
  2. If preferences genuinely need updating, use the explicit memory ops API (ops.apply with AddOp/EditOp) or direct user-facing edit tooling instead of the consolidator
  3. Check the slot registry/meta (load_l3_meta) for which slots are auto-consolidatable and drive off that flag

Example fix

// before
for slot in ALL_L3_SLOTS:
    result = run_update(slot, ...)

// after
for slot in ALL_L3_SLOTS:
    if slot == "preferences":
        continue  # user-authored; not auto-consolidated
    result = run_update(slot, ...)
Defensive patterns

Strategy: validation

Validate before calling

AUTO_CONSOLIDATABLE = {s for s in ALL_L3_SLOTS if s != "preferences"}
assert slot in AUTO_CONSOLIDATABLE, f"slot {slot!r} is not auto-consolidated"

Type guard

def is_auto_consolidatable(slot: str) -> bool:
    return slot != "preferences"

Try / catch

try:
    result = run_update(slot, ...)
except ValueError as e:
    if "not auto-consolidated" in str(e):
        logger.info("skipping consolidation for %s", slot)
    else:
        raise

Prevention

When it happens

Trigger: Calling run_update (or the consolidator manager's update mode) with slot='preferences'; e.g. enumerating L3 slots and forwarding each one to the update mode without filtering out 'preferences'.

Common situations: Writing a loop over all memory slots to consolidate them; adding a new L3 slot list that includes preferences; a UI/API layer exposing 'consolidate' for every slot indiscriminately.

Related errors


AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27). Data as JSON: /api/errors/e31620e378390a82. Report an issue: GitHub.