{"record":{"id":"0f0b842c9ee71e5f","repo":"HKUDS/DeepTutor","slug":"unknown-layer-layer-r-0f0b84","errorCode":null,"errorMessage":"unknown layer {layer!r}","messagePattern":"unknown layer (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/memory/consolidator/modes/update.py","lineNumber":132,"sourceCode":"                key,  # type: ignore[arg-type]\n                language=language,\n                user_label=user_label,\n                budget=budget if budget is not None else settings.update.l2_budget,\n                llm_selection=llm_selection,\n                on_event=on_event,\n                settings=settings,\n            )\n        if layer == \"L3\":\n            return await _run_update_l3(\n                key,  # type: ignore[arg-type]\n                language=language,\n                user_label=user_label,\n                budget=budget if budget is not None else settings.update.l3_budget,\n                llm_selection=llm_selection,\n                on_event=on_event,\n                settings=settings,\n            )\n        raise ValueError(f\"unknown layer {layer!r}\")\n    finally:\n        reset_llm_selection(token)\n\n\n# ── L2 ──────────────────────────────────────────────────────────────────\n\n\nasync def _run_update_l2(\n    surface: Surface,\n    *,\n    language: str,\n    user_label: str,\n    budget: int,\n    llm_selection: dict | None,\n    on_event: OnEvent | None,\n    settings,\n) -> UpdateResult:\n    meta = load_l2_meta(surface)","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/memory/consolidator/modes/update.py#L114-L150","documentation":"run_update dispatches consolidation per layer and supports only 'L2' and 'L3'. Any other layer string falls through to ValueError after the finally block resets the LLM selection token; both consolidate_l2 and consolidate_l3 delegate here.","triggerScenarios":"Calling run_update (directly or via consolidate_l2/consolidate_l3 wrappers) with a layer argument that is not exactly 'L2' or 'L3' — e.g. 'l2', 'L1', or a value from unvalidated config.","commonSituations":"Programmatic loops over layer names built from f-strings or user input; casing/format drift between the caller's constants and the consolidator's expected literals.","solutions":["Pass exactly 'L2' or 'L3'","Normalize and validate the layer string at the call site before invoking run_update","Centralize layer names in a Literal type or enum used by all consolidator modes"],"exampleFix":"# before\nresult = await run_update(\"l3\", slot, ...)\n# after\nresult = await run_update(\"L3\", slot, ...)","handlingStrategy":"type-guard","validationCode":"if layer not in (\"L2\", \"L3\"):\n    raise ValueError(f\"layer must be 'L2' or 'L3', got {layer!r}\")","typeGuard":"from typing import Literal, TypeGuard\n\nLayer = Literal[\"L2\", \"L3\"]\n\ndef is_layer(v: object) -> TypeGuard[Layer]:\n    return isinstance(v, str) and v in (\"L2\", \"L3\")","tryCatchPattern":"try:\n    await run_update(layer, slot)\nexcept ValueError as e:\n    if \"unknown layer\" in str(e):\n        return  # or normalize and retry\n    raise","preventionTips":["Use a shared Layer enum/Literal for all consolidator entry points","Validate layer at the boundary where it's parsed from config or CLI input"],"tags":["memory","consolidation","dispatch","argument-validation"],"backgroundTag":"unknown-layer-argument","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}