MemPalace/mempalace · error · ValueError

type must be a non-empty string

Error message

type must be a non-empty string

What it means

ValueError raised by _sanitize_event_type() when the event `type` is not a string or is empty/whitespace-only. This is the first guard before the regex check that enforces the event-type grammar (lowercase letters, digits, '.', '_', '-', max 64 chars).

Source

Thrown at mempalace/logstream.py:122

    and over-length values are still rejected.
    """
    if value is None or value == "":
        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

View on GitHub (pinned to 06cb6987f0)

Solutions

  1. Always pass a concrete type string like "agent.turn.start"
  2. Convert enums: type=EventKind.TURN_START.value
  3. Guard dynamic construction: type = parts and '.'.join(parts) or fallback — never emit with an empty type

Example fix

# before
events.emit(type="", stream="s", room="r")  # ValueError: type must be a non-empty string

# after
events.emit(type="agent.turn.start", stream="s", room="r")
Defensive patterns

Strategy: validation

Validate before calling

def valid_event_type(t) -> bool:
    return isinstance(t, str) and bool(t.strip())

Type guard

def is_event_type(value) -> bool:
    import re
    return (
        isinstance(value, str)
        and bool(value.strip())
        and bool(re.fullmatch(r"[a-z0-9._-]{1,64}", value.strip()))
    )

Try / catch

try:
    events.emit(type=t, stream=s, room=r)
except ValueError as e:
    if "type must be a non-empty string" in str(e):
        t = "event.untyped"  # safe default
        events.emit(type=t, stream=s, room=r)
    else:
        raise

Prevention

When it happens

Trigger: Calling the event API with type=None, type="", type=" ", or a non-string type value such as an int or enum object without str conversion.

Common situations: Building type dynamically and getting an empty join; passing a Python enum instead of .value; forgetting to set a default type in a wrapper; whitespace from templated f-strings.

Related errors


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