{"record":{"id":"6a415d252fa1348f","repo":"HKUDS/DeepTutor","slug":"add-text-length-must-be-1-max-text-len-got","errorCode":null,"errorMessage":"add: text length must be 1..{_MAX_TEXT_LEN} (got {len(op.text)})","messagePattern":"add: text length must be 1\\.\\.(.+?) \\(got (.+?)\\)","errorType":"validation","errorClass":"OpValidationError","httpStatus":null,"severity":"error","filePath":"deeptutor/services/memory/ops.py","lineNumber":75,"sourceCode":"@dataclass\nclass ApplyReport:\n    accepted: bool\n    results: list[OpResult] = field(default_factory=list)\n    reason: str = \"\"\n\n\nclass OpValidationError(Exception):\n    \"\"\"Raised when a batch fails pre-flight validation.\"\"\"\n\n\ndef _validate(doc: Document, ops: list[Op]) -> None:\n    edits: set[str] = set()\n    deletes: set[str] = set()\n\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                )","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/HKUDS/DeepTutor/blob/3e82f130422a813cdd73c10b21a44e9325f5821a/deeptutor/services/memory/ops.py#L57-L93","documentation":"OpValidationError from ops.apply's _validate: an AddOp's text must be non-empty and at most _MAX_TEXT_LEN characters. AddOp appends a new memory entry, and oversized/empty text would break the document format and L2 budgets, so it is rejected up front before any mutation occurs (validation is atomic — no partial apply).","triggerScenarios":"Constructing AddOp(text='', section=..., refs=[...]) or AddOp with text longer than _MAX_TEXT_LEN (typically an LLM generating a bloated or empty memory note) and passing it to ops.apply(doc, [op]).","commonSituations":"LLM-generated memory writes without length clamping; truncating user input to the wrong bound; unit tests exercising validation boundaries; copying constants from another module with a different _MAX_TEXT_LEN.","solutions":["Clamp or reject the text before building the op: if not text or len(text) > _MAX_TEXT_LEN, skip/split the write","Split very long content into multiple AddOps (each within the limit) or summarize it down","Import _MAX_TEXT_LEN from deeptutor.services.memory.ops and validate against the library's own constant instead of hardcoding"],"exampleFix":"# before\nop = AddOp(text=long_note, section=\"facts\", refs=[ref])\nresult = ops.apply(doc, [op])\n\n# after\nfrom deeptutor.services.memory.ops import _MAX_TEXT_LEN\nif long_note and len(long_note) <= _MAX_TEXT_LEN:\n    op = AddOp(text=long_note, section=\"facts\", refs=[ref])\n    result = ops.apply(doc, [op])","handlingStrategy":"validation","validationCode":"from deeptutor.services.memory.ops import _MAX_TEXT_LEN, OpValidationError\nassert isinstance(text, str) and 1 <= len(text) <= _MAX_TEXT_LEN","typeGuard":"def is_valid_add_text(text: str) -> bool:\n    return bool(text) and len(text) <= _MAX_TEXT_LEN","tryCatchPattern":"try:\n    result = ops.apply(doc, ops_list)\nexcept OpValidationError as e:\n    if e.args[0].startswith(\"add: text length\"):\n        text = text[:_MAX_TEXT_LEN]\n        ops_list = rebuild(ops_list, text=text)\n        result = ops.apply(doc, ops_list)\n    else:\n        raise","preventionTips":["Clamp LLM-generated text before building AddOp","Import length constants from ops.py rather than duplicating them","Validate the whole op batch before applying since apply is atomic"],"tags":["validation","memory","ops","length-limit"],"backgroundTag":"payload-validation-failed","analyzedSha":"3e82f130422a813cdd73c10b21a44e9325f5821a","analyzedAt":"2026-08-27T06:57:25.364Z","schemaVersion":2},"datasetVersion":"2026-08-27T08:17:20.692Z"}