{"record":{"id":"93347660b73b5aaa","repo":"HKUDS/DeepTutor","slug":"edit-target-id-op-target-id-not-found","errorCode":null,"errorMessage":"edit: target_id {op.target_id} not found","messagePattern":"edit: target_id (.+?) not found","errorType":"validation","errorClass":"OpValidationError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/memory/ops.py","lineNumber":89,"sourceCode":"\n    for op in ops:\n        if isinstance(op, AddOp):\n            if not op.text or len(op.text) > _MAX_TEXT_LEN:\n                raise OpValidationError(\n                    f\"add: text length must be 1..{_MAX_TEXT_LEN} (got {len(op.text)})\"\n                )\n            if not op.section or len(op.section) > _MAX_SECTION_LEN:\n                raise OpValidationError(f\"add: invalid section {op.section!r}\")\n            if not op.refs:\n                raise OpValidationError(\"add: refs must be non-empty\")\n            for ref in op.refs:\n                if not is_valid_ref(ref):\n                    raise OpValidationError(f\"add: malformed ref {ref!r}\")\n        elif isinstance(op, EditOp):\n            if not is_entry_id(op.target_id):\n                raise OpValidationError(f\"edit: malformed target_id {op.target_id!r}\")\n            if doc.find(op.target_id) is None:\n                raise OpValidationError(f\"edit: target_id {op.target_id} not found\")\n            if not op.new_text or len(op.new_text) > _MAX_TEXT_LEN:\n                raise OpValidationError(\n                    f\"edit: text length must be 1..{_MAX_TEXT_LEN} (got {len(op.new_text)})\"\n                )\n            if not op.new_refs:\n                raise OpValidationError(\"edit: refs must be non-empty\")\n            for ref in op.new_refs:\n                if not is_valid_ref(ref):\n                    raise OpValidationError(f\"edit: malformed ref {ref!r}\")\n            if op.target_id in deletes:\n                raise OpValidationError(\n                    f\"batch conflict: edit and delete on same id {op.target_id}\"\n                )\n            edits.add(op.target_id)\n        elif isinstance(op, DeleteOp):\n            if not is_entry_id(op.target_id):\n                raise OpValidationError(f\"delete: malformed target_id {op.target_id!r}\")\n            if doc.find(op.target_id) is None:","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/memory/ops.py#L71-L107","documentation":"OpValidationError raised in _validate when an EditOp's target_id is well-formed but doc.find(target_id) returns None — the entry being edited no longer (or never) exists in this document snapshot. Validation runs against the specific Document passed to ops.apply, so stale ids from an older version of the document are caught here, before any write.","triggerScenarios":"Editing based on a stale document: the entry was deleted or consolidated away after the caller read the doc, so the id isn't in the current Document passed to apply. Also passing the wrong Document (different KB/slot) to apply.","commonSituations":"Read-modify-write races with the consolidator or another session; long-lived cached entry lists in an agent loop; LLM editing an entry id from an earlier turn after memory changed; tests reusing fixtures across mutations.","solutions":["Re-read the document and re-resolve the entry id immediately before applying the edit; retry with the fresh id if the entry still exists","If the entry is gone, convert the edit into an AddOp of the new text instead of failing","Serialize memory writes (or use the run manager) so consolidation can't remove entries between read and apply"],"exampleFix":"# before\nop = EditOp(target_id=stale_id, new_text=new_note, new_refs=[ref])\nresult = ops.apply(old_doc, [op])\n\n# after\ndoc = load_doc()  # fresh snapshot\nif doc.find(stale_id) is None:\n    op = AddOp(text=new_note, section=last_known_section, refs=[ref])\nelse:\n    op = EditOp(target_id=stale_id, new_text=new_note, new_refs=[ref])\nresult = ops.apply(doc, [op])","handlingStrategy":"retry","validationCode":"doc = load_doc()  # fresh snapshot\nif doc.find(target_id) is None:\n    target_id = reresolve_entry(doc, old_text_matcher)\n    if target_id is None:\n        op = AddOp(text=new_text, section=section, refs=new_refs)","typeGuard":"def target_exists(doc, target_id: str) -> bool:\n    return doc.find(target_id) is not None","tryCatchPattern":"try:\n    ops.apply(doc, [op])\nexcept OpValidationError as e:\n    if \"not found\" in str(e):\n        doc = load_doc()\n        if doc.find(op.target_id) is not None:\n            ops.apply(doc, [op])  # retry on fresh snapshot\n        else:\n            ops.apply(doc, [to_add_op(op)])  # degrade to add\n    else:\n        raise","preventionTips":["Re-read the document right before applying edits","Fall back to AddOp when the entry disappeared","Serialize writes vs consolidation to avoid stale snapshots"],"tags":["validation","memory","ops","stale-state","optimistic-concurrency"],"backgroundTag":"stale-target-not-found","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}