mvanhorn/last30days-skill · error · ValueError
invalid library ID in {id_path}
Error message
invalid library ID in {id_path} What it means
Raised by get_or_create_library_id in library.py when the persisted library ID file (memory_dir/<LIBRARY_ID_FILENAME>) exists but its content does not match _LIBRARY_ID = r'[0-9a-f]{32}' — a 32-char lowercase hex string (a uuid4().hex). The ID namespaces one research library; a corrupt or hand-edited value breaks brief-name generation downstream, so it fails fast with ValueError naming the offending path.
Source
Thrown at skills/last30days/scripts/lib/library.py:90
return slug or "last30days"
def get_or_create_library_id(memory_dir: Path | str) -> str:
"""Return the persisted random namespace for one research library."""
memory_path = Path(memory_dir).expanduser()
memory_path.mkdir(parents=True, exist_ok=True)
id_path = memory_path / LIBRARY_ID_FILENAME
try:
library_id = id_path.read_text(encoding="utf-8").strip()
except FileNotFoundError:
library_id = uuid.uuid4().hex
try:
with id_path.open("x", encoding="utf-8") as handle:
handle.write(f"{library_id}\n")
except FileExistsError:
library_id = id_path.read_text(encoding="utf-8").strip()
if not _LIBRARY_ID.fullmatch(library_id):
raise ValueError(f"invalid library ID in {id_path}")
return library_id
def is_generated_brief_name(name: str) -> bool:
"""Return whether a filename has the exact library-renderer output shape."""
return _GENERATED_BRIEF_NAME.fullmatch(name) is not None
def scan_library(
memory_dir: Path | str = DEFAULT_MEMORY_DIR,
briefs_dir: Path | str = DEFAULT_BRIEFS_DIR,
) -> tuple[list[LibraryEntry], list[str]]:
"""Return valid saved entries and notes for files that could not be read.
Hand-edited and foreign files are tolerated: a generic Markdown heading is
enough to include a file, while unreadable or unrecognizable files are
skipped with a note instead of aborting the entire feed generation.
"""View on GitHub (pinned to c7460f6114)
Solutions
- Regenerate the ID: delete the ID file and re-run — get_or_create_library_id will create a fresh uuid4().hex via the exclusive-create path.
- Or restore the file content to a valid 32-lowercase-hex value if the original ID must be preserved (brief names embed it).
- Check what wrote the bad value (editor, sync tool, seeding script) so it does not recur.
Example fix
# before: id file contains
550E8400-E29B-41D4-A716-446655440000
# after: regenerate
rm ~/.config/last30days/memory/<library-id-file>
python -c "from last30days.scripts.lib.library import get_or_create_library_id; get_or_create_library_id('<memory_dir>')" Defensive patterns
Strategy: validation
Validate before calling
import re
from pathlib import Path
LIBRARY_ID = re.compile(r"[0-9a-f]{32}")
def library_id_ok(memory_dir: Path) -> bool:
id_file = memory_dir / LIBRARY_ID_FILENAME
return LIBRARY_ID.fullmatch(id_file.read_text(encoding="utf-8").strip()) is not None Type guard
import re
_LIBRARY_ID = re.compile(r"[0-9a-f]{32}")
def is_valid_library_id(value: object) -> bool:
return isinstance(value, str) and _LIBRARY_ID.fullmatch(value) is not None Try / catch
try:
lib_id = get_or_create_library_id(memory_dir)
except ValueError as e:
if 'invalid library ID' in str(e):
(memory_dir / LIBRARY_ID_FILENAME).unlink(missing_ok=True)
lib_id = get_or_create_library_id(memory_dir) # regenerates a fresh uuid4().hex Prevention
- Never hand-edit the library ID file; it must be 32 lowercase hex chars (uuid4().hex).
- Exclude the memory dir from sync/backup tools that rewrite dot-files in place.
- If seeding example memory dirs, generate the ID with uuid.uuid4().hex, not a placeholder string.
When it happens
Trigger: The ID file was hand-edited (uppercase UUID with dashes pasted in, e.g. '550E8400-E29B-...'), truncated by an interrupted write, filled with a placeholder like 'my-library', or corrupted by disk/sync tools; a non-UTF-8 file would surface earlier at read_text.
Common situations: Users open the file out of curiosity and 'fix' the ID; cloud-sync or backup tools mangle dot-files; scripts seed the memory dir with example content including a fake ID; partial writes from a crashed first run.
Related errors
- save_output: could not find a unique filename after 101 atte
- [last30days] Cannot read --synthesis-file: {exc}\n
- [CompetitorsPlan] Cannot read plan file: {exc}\n
- Could not find a unique discovery output filename
- [Planner] Cannot read --plan file: {exc}\n
AI-assisted analysis of mvanhorn/last30days-skill@c7460f6114 (2026-08-15).
Data as JSON: /api/errors/baf8aeb32486f626.
Report an issue: GitHub.