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

  1. Lowercase the value and restrict it to a-z, 0-9, '.', '_', '-' (e.g. 'task.request', 'patch.ready').
  2. Check length: strip and count characters; keep it at 64 chars or fewer and start with a letter or digit.
  3. Replace separators like ':', '/', ' ' with '.' or '-' before calling (e.g. 'Fix/Search' -> 'fix.search').
  4. 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

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


AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15). Data as JSON: /api/errors/fe305da93cb68f9a. Report an issue: GitHub.