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
- Always pass a concrete type string like "agent.turn.start"
- Convert enums: type=EventKind.TURN_START.value
- 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
- Use a fixed vocabulary of event types (constants/enum) instead of building them ad hoc
- Convert enums with .value before emitting
- Check dynamically-built types with a fullmatch on [a-z0-9._-]{1,64} before emit
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
- {field_name} must be a non-empty string
- type={value!r} is not a valid event type (lowercase letters,
- {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/f859aeed6f3a68c1.
Report an issue: GitHub.