{"record":{"id":"37d010ae46089f0b","repo":"shareAI-lab/learn-claude-code","slug":"memory-description-and-body-cannot-be-empty","errorCode":null,"errorMessage":"Memory description and body cannot be empty","messagePattern":"Memory description and body cannot be empty","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"s09_memory/code.py","lineNumber":155,"sourceCode":"        if _normalized_memory_text(str(memory.get(\"body\", \"\"))) == normalized_body:\n            return False\n    return True\n\ndef memory_document(name: str, mem_type: str, description: str, body: str) -> str:\n    metadata = yaml.safe_dump(\n        {\"name\": name, \"description\": description, \"type\": mem_type},\n        sort_keys=False,\n        allow_unicode=True,\n    ).strip()\n    return f\"---\\n{metadata}\\n---\\n\\n{body.strip()}\\n\"\n\ndef write_memory_file(name: str, mem_type: str, description: str, body: str) -> Path:\n    if not name.strip():\n        raise ValueError(\"Memory name cannot be empty\")\n    if mem_type not in MEMORY_TYPES:\n        raise ValueError(f\"Unknown memory type: {mem_type}\")\n    if not description.strip() or not body.strip():\n        raise ValueError(\"Memory description and body cannot be empty\")\n\n    MEMORY_DIR.mkdir(parents=True, exist_ok=True)\n    path = memory_path(f\"{memory_slug(name)}.md\")\n    path.write_text(memory_document(name, mem_type, description, body))\n    rebuild_memory_index()\n    return path\n\ndef rebuild_memory_index() -> None:\n    MEMORY_DIR.mkdir(parents=True, exist_ok=True)\n    lines = []\n    for path in sorted(MEMORY_DIR.glob(\"*.md\")):\n        if path.name == MEMORY_INDEX.name:\n            continue\n        try:\n            path = memory_path(path.name)\n        except ValueError:\n            continue\n        metadata, body = parse_frontmatter(path.read_text())","sourceCodeStart":137,"sourceCodeEnd":173,"githubUrl":"https://github.com/shareAI-lab/learn-claude-code/blob/985456f4adea6f4df8fbad4112245dbd97444eae/s09_memory/code.py#L137-L173","documentation":"Raised by write_memory_file() in s09_memory/code.py:155 when either the description or the body argument is empty after stripping whitespace. Both fields are required because the description feeds the memory index (rebuilt immediately after the write) and the body is the actual record content; a record with no body would be a dead file in the store. One combined check covers both fields, so the message does not say which one is missing.","triggerScenarios":"Calling write_memory_file with description='' or body='   '; passing a description but a body that is None (AttributeError aside, whitespace-only strings are the direct trigger); building records from LLM output where the body key exists but contains only whitespace or formatting characters.","commonSituations":"Summarization pipelines whose output got truncated to empty; template-based record writers that render an empty template when a variable is missing; tests that stub out content generation with blank strings.","solutions":["Inspect both arguments with strip() before the call and reject or repair whichever is blank.","When generating records from model output, run validate_memory_record() first — it rejects empty description/body — or log and skip the record instead of writing it.","If a body is genuinely short (e.g. a one-word preference), still provide a full sentence so strip() cannot empty it."],"exampleFix":"# before\nwrite_memory_file(name, mem_type, record.get('description', ''), record.get('body', ''))\n\n# after\ndescription = (record.get('description') or '').strip()\nbody = (record.get('body') or '').strip()\nif description and body:\n    write_memory_file(name, mem_type, description, body)\nelse:\n    print(f'skipped record {name!r}: empty description or body')","handlingStrategy":"validation","validationCode":"def record_is_writable(name, mem_type, description, body) -> bool:\n    return all(isinstance(v, str) and v.strip()\n               for v in (name, description, body))","typeGuard":"def has_full_record(rec: dict) -> bool:\n    return all(isinstance(rec.get(k), str) and rec[k].strip()\n               for k in ('name', 'description', 'body'))","tryCatchPattern":"try:\n    write_memory_file(name, mem_type, description, body)\nexcept ValueError:\n    log.info('skipping incomplete memory record %r', name)\n    continue","preventionTips":["Strip and check both description and body at the call site; the error does not say which is blank.","Never write records straight from LLM output without validate_memory_record().","Skip-and-log incomplete records during capture rather than aborting the batch."],"tags":["validation","memory","user-input"],"backgroundTag":null,"analyzedSha":"985456f4adea6f4df8fbad4112245dbd97444eae","analyzedAt":"2026-08-14T22:02:26.028Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}