{"record":{"id":"f859aeed6f3a68c1","repo":"MemPalace/mempalace","slug":"type-must-be-a-non-empty-string","errorCode":null,"errorMessage":"type must be a non-empty string","messagePattern":"type must be a non-empty string","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mempalace/logstream.py","lineNumber":122,"sourceCode":"    and over-length values are still rejected.\n    \"\"\"\n    if value is None or value == \"\":\n        if required:\n            raise ValueError(f\"{field_name} must be a non-empty string\")\n        return None\n    if not isinstance(value, str) or not value.strip():\n        raise ValueError(f\"{field_name} must be a non-empty string\")\n    value = strip_lone_surrogates(value.strip())\n    if len(value) > _MAX_ROUTING_LENGTH:\n        raise ValueError(f\"{field_name} exceeds maximum length of {_MAX_ROUTING_LENGTH} characters\")\n    if any(ord(ch) < 0x20 or ch == \"\\x7f\" for ch in value):\n        raise ValueError(f\"{field_name} contains control characters\")\n    return value\n\n\ndef _sanitize_event_type(value) -> str:\n    if not isinstance(value, str) or not value.strip():\n        raise ValueError(\"type must be a non-empty string\")\n    value = value.strip()\n    if not _EVENT_TYPE_RE.match(value):\n        raise ValueError(\n            f\"type={value!r} is not a valid event type \"\n            \"(lowercase letters, digits, '.', '_', '-'; max 64 chars)\"\n        )\n    return value\n\n\ndef _sanitize_status(value) -> Optional[str]:\n    if value is None or value == \"\":\n        return None\n    if not isinstance(value, str) or value not in EVENT_STATUSES:\n        allowed = \", \".join(sorted(EVENT_STATUSES))\n        raise ValueError(f\"status={value!r} is not one of: {allowed}\")\n    return value\n\n","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/MemPalace/mempalace/blob/06cb6987f02610784fefbad4b2bd5d026d164ba6/mempalace/logstream.py#L104-L140","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\nevents.emit(type=\"\", stream=\"s\", room=\"r\")  # ValueError: type must be a non-empty string\n\n# after\nevents.emit(type=\"agent.turn.start\", stream=\"s\", room=\"r\")","handlingStrategy":"validation","validationCode":"def valid_event_type(t) -> bool:\n    return isinstance(t, str) and bool(t.strip())","typeGuard":"def is_event_type(value) -> bool:\n    import re\n    return (\n        isinstance(value, str)\n        and bool(value.strip())\n        and bool(re.fullmatch(r\"[a-z0-9._-]{1,64}\", value.strip()))\n    )","tryCatchPattern":"try:\n    events.emit(type=t, stream=s, room=r)\nexcept ValueError as e:\n    if \"type must be a non-empty string\" in str(e):\n        t = \"event.untyped\"  # safe default\n        events.emit(type=t, stream=s, room=r)\n    else:\n        raise","preventionTips":["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"],"tags":["logstream","validation","event-type","required-field"],"backgroundTag":null,"analyzedSha":"06cb6987f02610784fefbad4b2bd5d026d164ba6","analyzedAt":"2026-08-15T03:03:36.213Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}