HKUDS/DeepTutor · error · OpValidationError
add: malformed ref {ref!r}
Error message
add: malformed ref {ref!r} What it means
OpValidationError raised in _validate when one of an AddOp's refs fails is_valid_ref. Refs use a constrained grammar (a validated reference format identifying source messages/spans), and any string outside that grammar is rejected so entries never carry unparseable provenance.
Source
Thrown at deeptutor/services/memory/ops.py:84
def _validate(doc: Document, ops: list[Op]) -> None:
edits: set[str] = set()
deletes: set[str] = set()
for op in ops:
if isinstance(op, AddOp):
if not op.text or len(op.text) > _MAX_TEXT_LEN:
raise OpValidationError(
f"add: text length must be 1..{_MAX_TEXT_LEN} (got {len(op.text)})"
)
if not op.section or len(op.section) > _MAX_SECTION_LEN:
raise OpValidationError(f"add: invalid section {op.section!r}")
if not op.refs:
raise OpValidationError("add: refs must be non-empty")
for ref in op.refs:
if not is_valid_ref(ref):
raise OpValidationError(f"add: malformed ref {ref!r}")
elif isinstance(op, EditOp):
if not is_entry_id(op.target_id):
raise OpValidationError(f"edit: malformed target_id {op.target_id!r}")
if doc.find(op.target_id) is None:
raise OpValidationError(f"edit: target_id {op.target_id} not found")
if not op.new_text or len(op.new_text) > _MAX_TEXT_LEN:
raise OpValidationError(
f"edit: text length must be 1..{_MAX_TEXT_LEN} (got {len(op.new_text)})"
)
if not op.new_refs:
raise OpValidationError("edit: refs must be non-empty")
for ref in op.new_refs:
if not is_valid_ref(ref):
raise OpValidationError(f"edit: malformed ref {ref!r}")
if op.target_id in deletes:
raise OpValidationError(
f"batch conflict: edit and delete on same id {op.target_id}"
)View on GitHub (pinned to 3e82f13042)
Solutions
- Build refs only with the library's own helper/constructor for refs (or copy the exact format is_valid_ref enforces — read its implementation in ops.py)
- Validate each ref with is_valid_ref before constructing the op and drop/repair failures
- If ids are user/LLM supplied, sanitize them (strip, charset-check) before embedding in a ref
Example fix
# before
op = AddOp(text=note, section="facts", refs=[f"source {msg_id}"])
# after
from deeptutor.services.memory.ops import is_valid_ref
ref = f"msg:{msg_id}"
assert is_valid_ref(ref), f"bad ref: {ref}"
op = AddOp(text=note, section="facts", refs=[ref]) Defensive patterns
Strategy: validation
Validate before calling
from deeptutor.services.memory.ops import is_valid_ref
refs = [r for r in raw_refs if is_valid_ref(r)]
if not refs:
raise ValueError("no valid refs after filtering") Type guard
def refs_are_valid(refs) -> bool:
return bool(refs) and all(is_valid_ref(r) for r in refs) Try / catch
try:
ops.apply(doc, [op])
except OpValidationError as e:
if "malformed ref" in str(e):
op = replace(op, refs=[make_ref(source)])
ops.apply(doc, [op])
else:
raise Prevention
- Build refs only via the library's ref format/helper
- Filter refs through is_valid_ref at the boundary
- Never let free-form LLM text become a ref verbatim
When it happens
Trigger: AddOp(..., refs=["some arbitrary string"]) where the string doesn't match the ref format — e.g. passing raw URLs, plain message text, 'msg: ' with an invalid id, or a ref built by string concatenation with a typo.
Common situations: LLM free-typing refs in a tool call; constructing refs from ids containing whitespace/invalid characters; version drift where the ref grammar changed but callers still emit the old format.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- add: text length must be 1..{_MAX_TEXT_LEN} (got {len(op.tex
- add: invalid section {op.section!r}
- add: refs must be non-empty
- edit: malformed target_id {op.target_id!r}
- edit: target_id {op.target_id} not found
AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27).
Data as JSON: /api/errors/c98e04270cb6b0a6.
Report an issue: GitHub.