MemPalace/mempalace · error · ValueError
type={value!r} is not a valid event type (lowercase letters,
Error message
type={value!r} is not a valid event type (lowercase letters, digits, '.', '_', '-'; max 64 chars) What it means
append_event (or any API taking an event type) rejected the `type` argument because it does not match the pattern ^[a-z0-9][a-z0-9_.-]{0,63}$. Event types are structured routing keys (e.g. 'task.request', 'patch.ready'), so the logstream enforces lowercase letters, digits, '.', '_', '-' with a 64-character maximum rather than accepting free-form strings. The value is stripped before matching, but whitespace inside, uppercase letters, '/', ':', and leading punctuation all fail.
Source
Thrown at mempalace/logstream.py:125
if required:
raise ValueError(f"{field_name} must be a non-empty string")
return None
if not isinstance(value, str) or not value.strip():
raise ValueError(f"{field_name} must be a non-empty string")
value = strip_lone_surrogates(value.strip())
if len(value) > _MAX_ROUTING_LENGTH:
raise ValueError(f"{field_name} exceeds maximum length of {_MAX_ROUTING_LENGTH} characters")
if any(ord(ch) < 0x20 or ch == "\x7f" for ch in value):
raise ValueError(f"{field_name} contains control characters")
return value
def _sanitize_event_type(value) -> str:
if not isinstance(value, str) or not value.strip():
raise ValueError("type must be a non-empty string")
value = value.strip()
if not _EVENT_TYPE_RE.match(value):
raise ValueError(
f"type={value!r} is not a valid event type "
"(lowercase letters, digits, '.', '_', '-'; max 64 chars)"
)
return value
def _sanitize_status(value) -> Optional[str]:
if value is None or value == "":
return None
if not isinstance(value, str) or value not in EVENT_STATUSES:
allowed = ", ".join(sorted(EVENT_STATUSES))
raise ValueError(f"status={value!r} is not one of: {allowed}")
return value
def _sanitize_body(value, max_bytes: int, field_name: str = "body") -> str:
"""Validate verbatim payload text. Empty is allowed; ``None`` becomes ''."""
if value is None:View on GitHub (pinned to 06cb6987f0)
Solutions
- Lowercase the value and restrict it to a-z, 0-9, '.', '_', '-' (e.g. 'task.request', 'patch.ready').
- Check length: strip and count characters; keep it at 64 chars or fewer and start with a letter or digit.
- Replace separators like ':', '/', ' ' with '.' or '-' before calling (e.g. 'Fix/Search' -> 'fix.search').
- Validate against the same regex before the call: re.match(r'^[a-z0-9][a-z0-9_.-]{0,63}$', type).
Example fix
// before evt = ls.append_event(type="Task.Request:V2", ...) // after evt = ls.append_event(type="task.request.v2", ...)
Defensive patterns
Strategy: validation
Validate before calling
import re
EVENT_TYPE_RE = re.compile(r"^[a-z0-9][a-z0-9_.-]{0,63}$")
def valid_event_type(t):
return isinstance(t, str) and bool(EVENT_TYPE_RE.match(t.strip()))
# before the call
if not valid_event_type(evt_type):
evt_type = re.sub(r"[^a-z0-9._-]", ".", evt_type.lower().strip())[:64] Type guard
def is_event_type(value) -> bool:
return isinstance(value, str) and bool(__import__("re").match(r"^[a-z0-9][a-z0-9_.-]{0,63}$", value.strip())) Try / catch
try:
evt = ls.append_event(type=evt_type, ...)
except ValueError as e:
if "not a valid event type" in str(e):
evt_type = re.sub(r"[^a-z0-9._-]", ".", evt_type.lower().strip())[:64]
evt = ls.append_event(type=evt_type, ...)
else:
raise Prevention
- Centralize event-type construction in one helper that lowercases and slugifies.
- Document the allowed pattern next to every producer integration point.
- Add a unit test asserting your producer's emitted types match ^[a-z0-9][a-z0-9_.-]{0,63}$.
When it happens
Trigger: Calling ls.append_event(type='Task.Request', ...) (uppercase); type='task:request' (colon); type='task request' (space); type='.request' (leading dot fails the first-char class); type longer than 64 chars; type='' or None after strip; passing a filter type to list_events/wait_events with the same shape problems (both run _sanitize_event_type).
Common situations: Agents deriving event types from prose or file paths (e.g. 'Fix/Search' with a slash), copying type names from JSON keys with camelCase, or non-ASCII locales producing uppercase/Unicode variants. Also seen when a caller interpolates a version number like 'task.request.v2//beta'.
Related errors
- type must be a non-empty string
- {field_name} must be a non-empty string
- {field_name} exceeds maximum length of {_MAX_ROUTING_LENGTH}
- {field_name} contains control characters
- status={value!r} is not one of: {allowed}
AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15).
Data as JSON: /api/errors/fe305da93cb68f9a.
Report an issue: GitHub.