Skip to content

osrlib.crawl.events

The crawl event types, the combined registry, and the any-event parser.

Crawl events subclass the core Event base, inheriting the emission contract (frozen, extra="ignore", dotted snake_case codes, declared outcome-bearing code sets, visibility). CRAWL_EVENT_CLASSES joins the kernel tuple in ALL_EVENT_CLASSES and the AnyEvent union; parse_any_event covers both and the session log uses it.

Visibility follows B/X's hidden-roll doctrine: referee-rolled dice (detection, surprise, reaction, wandering checks) are referee events, and the player-facing events carry behavior and outcomes only — a silent listen is genuinely ambiguous.

ALL_EVENT_CLASSES module-attribute

ALL_EVENT_CLASSES: tuple[type[Event], ...] = (*KERNEL_EVENT_CLASSES, *CRAWL_EVENT_CLASSES)

Every event class the library emits — kernel then crawl, in declaration order.

AnyEvent module-attribute

AnyEvent = Annotated[Union[*ALL_EVENT_CLASSES,], Field(discriminator='event_type')]

Any library event, discriminated by event_type.

AdventureXpAwardEvent

Bases: Event

The end-of-adventure XP award: the totals and the per-head share.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.xp.adventure_award'})

event_type class-attribute instance-attribute

event_type: Literal['adventure_xp_award'] = 'adventure_xp_award'

code class-attribute instance-attribute

code: str = 'session.xp.adventure_award'

visibility class-attribute instance-attribute

monster_xp instance-attribute

monster_xp: int

treasure_xp instance-attribute

treasure_xp: int

share instance-attribute

share: int

survivors instance-attribute

survivors: tuple[str, ...]

BattleEndedEvent

Bases: Event

The battle ended: victory, the party fled, or defeat.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.ended.victory', 'battle.ended.fled', 'battle.ended.defeat'})

event_type class-attribute instance-attribute

event_type: Literal['battle_ended'] = 'battle_ended'

visibility class-attribute instance-attribute

BattleRoundEvent

Bases: Event

A battle round began.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.round.started'})

event_type class-attribute instance-attribute

event_type: Literal['battle_round'] = 'battle_round'

code class-attribute instance-attribute

code: str = 'battle.round.started'

visibility class-attribute instance-attribute

round instance-attribute

round: int

BattleStartedEvent

Bases: Event

Battle began: the range-track machine takes over.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.started'})

event_type class-attribute instance-attribute

event_type: Literal['battle_started'] = 'battle_started'

code class-attribute instance-attribute

code: str = 'battle.started'

visibility class-attribute instance-attribute

CurseRevealedEvent

Bases: Event

A cursed item revealed its true nature — and pins itself to its bearer.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'items.curse.revealed'})

event_type class-attribute instance-attribute

event_type: Literal['curse_revealed'] = 'curse_revealed'

code class-attribute instance-attribute

code: str = 'items.curse.revealed'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

instance_id instance-attribute

instance_id: str

template_id instance-attribute

template_id: str

DetectionRolledEvent

Bases: Event

A referee-rolled detection die: search, listen, and trap-spring checks.

Rolled whether or not anything is there (the no-leak convention); roll is None when a zero chance consumed no die.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.detection.rolled'})

event_type class-attribute instance-attribute

event_type: Literal['detection_rolled'] = 'detection_rolled'

code class-attribute instance-attribute

code: str = 'exploration.detection.rolled'

visibility class-attribute instance-attribute

character_id class-attribute instance-attribute

character_id: str | None = None

kind instance-attribute

kind: str

chance instance-attribute

chance: int

roll class-attribute instance-attribute

roll: int | None = None

passed instance-attribute

passed: bool

DiceRolledEvent

Bases: Event

An authorial dice roll resolved (referee — the referee's hidden adjudication rolls).

Emitted by RollDice when a referee resolves a freeform chance outcome by rolling through the seeded session. Carries the expression that was rolled, the total, and each individual die result in rolls.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'adjudication.dice_rolled'})

event_type class-attribute instance-attribute

event_type: Literal['dice_rolled'] = 'dice_rolled'

code class-attribute instance-attribute

code: str = 'adjudication.dice_rolled'

visibility class-attribute instance-attribute

expression instance-attribute

expression: str

total instance-attribute

total: int

rolls instance-attribute

rolls: tuple[int, ...]

DoorEvent

Bases: Event

A door changed state; the edge is named by its cell and direction.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset(
    {
        "exploration.door.opened",
        "exploration.door.closed",
        "exploration.door.forced",
        "exploration.door.stuck",
        "exploration.door.wedged",
        "exploration.door.swung_shut",
        "exploration.door.unlocked",
    }
)

event_type class-attribute instance-attribute

event_type: Literal['door'] = 'door'

visibility class-attribute instance-attribute

x instance-attribute

x: int

y instance-attribute

y: int

direction instance-attribute

direction: str

character_id class-attribute instance-attribute

character_id: str | None = None

EncounterEndedEvent

Bases: Event

The encounter concluded; the clock owes at least one full turn.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.ended'})

event_type class-attribute instance-attribute

event_type: Literal['encounter_ended'] = 'encounter_ended'

code class-attribute instance-attribute

code: str = 'encounter.ended'

visibility class-attribute instance-attribute

outcome instance-attribute

outcome: str

EncounterStartedEvent

Bases: Event

An encounter opened: visible monster names and counts only.

The surprise rolls are referee events; the outcomes ride here — being surprised is felt in the fiction.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.started'})

event_type class-attribute instance-attribute

event_type: Literal['encounter_started'] = 'encounter_started'

code class-attribute instance-attribute

code: str = 'encounter.started'

visibility class-attribute instance-attribute

monster_name instance-attribute

monster_name: str

count instance-attribute

count: int

distance_feet instance-attribute

distance_feet: int

party_surprised class-attribute instance-attribute

party_surprised: bool = False

monsters_surprised class-attribute instance-attribute

monsters_surprised: bool = False

EvasionEvent

Bases: Event

An evasion attempt resolved: immediate success, or a pursuit begins.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.evasion.succeeded', 'encounter.evasion.pursuit'})

event_type class-attribute instance-attribute

event_type: Literal['evasion'] = 'evasion'

visibility class-attribute instance-attribute

ExhaustionEvent

Bases: Event

The party gained or recovered from running exhaustion (30 rounds, −2s).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.exhaustion.gained', 'encounter.exhaustion.recovered'})

event_type class-attribute instance-attribute

event_type: Literal['exhaustion'] = 'exhaustion'

visibility class-attribute instance-attribute

FatigueEvent

Bases: Event

The party gained or recovered from the unrested-fatigue penalty.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.fatigue.gained', 'exploration.fatigue.recovered'})

event_type class-attribute instance-attribute

event_type: Literal['fatigue'] = 'fatigue'

visibility class-attribute instance-attribute

FlagSetEvent

Bases: Event

A session flag changed (referee — content wiring is the game's secret).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.flag.set'})

event_type class-attribute instance-attribute

event_type: Literal['flag_set'] = 'flag_set'

code class-attribute instance-attribute

code: str = 'session.flag.set'

visibility class-attribute instance-attribute

key instance-attribute

key: str

value instance-attribute

value: str | int | bool

GameOverEvent

Bases: Event

The session reached its terminal state (TPK).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.game_over'})

event_type class-attribute instance-attribute

event_type: Literal['game_over'] = 'game_over'

code class-attribute instance-attribute

code: str = 'session.game_over'

visibility class-attribute instance-attribute

reason instance-attribute

reason: str

GroupMovedEvent

Bases: Event

A group's range-track distance changed.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.group.moved'})

event_type class-attribute instance-attribute

event_type: Literal['group_moved'] = 'group_moved'

code class-attribute instance-attribute

code: str = 'battle.group.moved'

visibility class-attribute instance-attribute

group_id instance-attribute

group_id: str

distance_feet instance-attribute

distance_feet: int

HealingPurchasedEvent

Bases: Event

A temple healing service purchased and cast.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'town.healing.purchased'})

event_type class-attribute instance-attribute

event_type: Literal['healing_purchased'] = 'healing_purchased'

code class-attribute instance-attribute

code: str = 'town.healing.purchased'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

service instance-attribute

service: str

cost_gp instance-attribute

cost_gp: int

HoardGeneratedEvent

Bases: Event

A lair hoard, carried bundle, or area treasure generated (referee).

Referee visibility — contents are itemized here and players learn by finding. cache_ref is the engine-created cache's state reference (or the group id for carried bundles); value and counts summarize the generation.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'treasure.hoard.generated'})

event_type class-attribute instance-attribute

event_type: Literal['hoard_generated'] = 'hoard_generated'

code class-attribute instance-attribute

code: str = 'treasure.hoard.generated'

visibility class-attribute instance-attribute

cache_ref instance-attribute

cache_ref: str

treasure_types class-attribute instance-attribute

treasure_types: tuple[str, ...] = ()

coins_gp_value class-attribute instance-attribute

coins_gp_value: int = 0

valuable_ids class-attribute instance-attribute

valuable_ids: tuple[str, ...] = ()

magic_item_ids class-attribute instance-attribute

magic_item_ids: tuple[str, ...] = ()

ItemAcquiredEvent

Bases: Event

Items or coins entered a character's inventory.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.item.acquired'})

event_type class-attribute instance-attribute

event_type: Literal['item_acquired'] = 'item_acquired'

code class-attribute instance-attribute

code: str = 'exploration.item.acquired'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

item_ids class-attribute instance-attribute

item_ids: tuple[str, ...] = ()

coins_gp_value class-attribute instance-attribute

coins_gp_value: int = 0

ItemIdentifiedEvent

Bases: Event

A magic item identified — a first meaningful use of it is the trigger.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'items.item.identified'})

event_type class-attribute instance-attribute

event_type: Literal['item_identified'] = 'item_identified'

code class-attribute instance-attribute

code: str = 'items.item.identified'

visibility class-attribute instance-attribute

instance_id instance-attribute

instance_id: str

template_id instance-attribute

template_id: str

ItemUsedEvent

Bases: Event

A magic item used: a potion drunk (or mixed), a scroll read, a device activated.

items.device.inert is a rejection code, not an event — activating an exhausted device costs nothing, the same as a blocked move. Charges never appear here: they are referee-only forever (RAW, undiscoverable).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset(
    {"items.potion.drunk", "items.potion.mixed", "items.scroll.read", "items.scroll.cursed", "items.device.activated"}
)

event_type class-attribute instance-attribute

event_type: Literal['item_used'] = 'item_used'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

instance_id instance-attribute

instance_id: str

manual class-attribute instance-attribute

manual: tuple[str, ...] = ()

ItemsDroppedEvent

Bases: Event

Items or coins dropped onto the party's cell (or the pursuit trail).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.item.dropped'})

event_type class-attribute instance-attribute

event_type: Literal['items_dropped'] = 'items_dropped'

code class-attribute instance-attribute

code: str = 'exploration.item.dropped'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

item_ids class-attribute instance-attribute

item_ids: tuple[str, ...] = ()

coins_gp_value class-attribute instance-attribute

coins_gp_value: int = 0

LightEvent

Bases: Event

A light source changed state.

source is the item or effect kind (torch, lantern, light); .failed is a failed tinder-box attempt; .expired is the session's player-facing translation of the ledger's referee-visibility expiry.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset(
    {"exploration.light.lit", "exploration.light.extinguished", "exploration.light.failed", "exploration.light.expired"}
)

event_type class-attribute instance-attribute

event_type: Literal['light'] = 'light'

visibility class-attribute instance-attribute

character_id class-attribute instance-attribute

character_id: str | None = None

source instance-attribute

source: str

ListenedEvent

Bases: Event

What the listener heard — heard-something or silence, genuinely ambiguous.

Undead make no noise, so the referee-side roll (which rides DetectionRolledEvent) happens whether or not anything is there; silence never says which.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.listen.heard', 'exploration.listen.silent'})

event_type class-attribute instance-attribute

event_type: Literal['listened'] = 'listened'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

direction instance-attribute

direction: str

LocationEnteredEvent

Bases: Event

The party crossed a location boundary.

location_kind is area, level, dungeon, or town; location_id is the area or dungeon id ("town" for town). level_number rides level and dungeon entries.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.location.entered'})

event_type class-attribute instance-attribute

event_type: Literal['location_entered'] = 'location_entered'

code class-attribute instance-attribute

code: str = 'exploration.location.entered'

visibility class-attribute instance-attribute

location_kind instance-attribute

location_kind: str

location_id instance-attribute

location_id: str

level_number class-attribute instance-attribute

level_number: int | None = None

MonsterDefeatedEvent

Bases: Event

One monster defeated — feeds the adventure's XP award.

Emitted per monster at battle end with outcome slain, routed, or surrendered; xp is the template's printed award.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.monster.defeated'})

event_type class-attribute instance-attribute

event_type: Literal['monster_defeated'] = 'monster_defeated'

code class-attribute instance-attribute

code: str = 'battle.monster.defeated'

visibility class-attribute instance-attribute

monster_id instance-attribute

monster_id: str

template_id instance-attribute

template_id: str

outcome instance-attribute

outcome: str

xp instance-attribute

xp: int

MonsterFledEvent

Bases: Event

A monster group broke: fled the battle or surrendered.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.side.fled', 'battle.side.surrendered'})

event_type class-attribute instance-attribute

event_type: Literal['monster_fled'] = 'monster_fled'

visibility class-attribute instance-attribute

group_id instance-attribute

group_id: str

MonstersSpawnedEvent

Bases: Event

Monsters spawned into the session registry (referee bookkeeping).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.monsters.spawned'})

event_type class-attribute instance-attribute

event_type: Literal['monsters_spawned'] = 'monsters_spawned'

code class-attribute instance-attribute

code: str = 'session.monsters.spawned'

visibility class-attribute instance-attribute

template_id instance-attribute

template_id: str

monster_ids instance-attribute

monster_ids: tuple[str, ...]

NpcPartySpawnedEvent

Bases: Event

An NPC adventuring party generated and fielded (referee — the full roster).

The player-facing EncounterStartedEvent names "adventurers" and the count; the roster, classes, and levels are the referee's.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.npc_party.spawned'})

event_type class-attribute instance-attribute

event_type: Literal['npc_party_spawned'] = 'npc_party_spawned'

code class-attribute instance-attribute

code: str = 'encounter.npc_party.spawned'

visibility class-attribute instance-attribute

party_kind instance-attribute

party_kind: str

npc_ids instance-attribute

npc_ids: tuple[str, ...]

class_ids instance-attribute

class_ids: tuple[str, ...]

levels instance-attribute

levels: tuple[int, ...]

alignment instance-attribute

alignment: str

PartyMovedEvent

Bases: Event

The party moved or turned; x/y/facing are the resulting pose.

A blocked move is a rejection (exploration.move.blocked), never an event: moving into a wall is an in-fiction invalid command, not a game state change.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.party.moved', 'exploration.party.turned'})

event_type class-attribute instance-attribute

event_type: Literal['party_moved'] = 'party_moved'

visibility class-attribute instance-attribute

x instance-attribute

x: int

y instance-attribute

y: int

facing instance-attribute

facing: str

ProvisionsEvent

Bases: Event

A day-boundary provision outcome: consumed, or short (food or water).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.provisions.consumed', 'exploration.provisions.short'})

event_type class-attribute instance-attribute

event_type: Literal['provisions'] = 'provisions'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

kind instance-attribute

kind: str

PursuitEvent

Bases: Event

One pursuit beat: the round's gap, a distraction, escape, or capture.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset(
    {"encounter.pursuit.round", "encounter.pursuit.distracted", "encounter.pursuit.escaped", "encounter.pursuit.caught"}
)

event_type class-attribute instance-attribute

event_type: Literal['pursuit'] = 'pursuit'

visibility class-attribute instance-attribute

round instance-attribute

round: int

gap_feet instance-attribute

gap_feet: int

RestedEvent

Bases: Event

A rest completed or was interrupted; kind is turn, night, or day.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.rest.rested', 'exploration.rest.interrupted'})

event_type class-attribute instance-attribute

event_type: Literal['rested'] = 'rested'

visibility class-attribute instance-attribute

kind instance-attribute

kind: str

SearchCompletedEvent

Bases: Event

A search finished: what it revealed, or nothing (which is ambiguous).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.search.found', 'exploration.search.nothing'})

event_type class-attribute instance-attribute

event_type: Literal['search_completed'] = 'search_completed'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

kind instance-attribute

kind: str

found class-attribute instance-attribute

found: tuple[str, ...] = ()

SpellDeclaredEvent

Bases: Event

A spell declaration posted — table-visible per RAW.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'battle.spell.declared'})

event_type class-attribute instance-attribute

event_type: Literal['spell_declared'] = 'spell_declared'

code class-attribute instance-attribute

code: str = 'battle.spell.declared'

visibility class-attribute instance-attribute

caster_id instance-attribute

caster_id: str

spell_id instance-attribute

spell_id: str

reversed class-attribute instance-attribute

reversed: bool = False

StanceChangedEvent

Bases: Event

The monsters' stance, as behavior — the reaction roll itself is referee.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.stance.changed'})

event_type class-attribute instance-attribute

event_type: Literal['stance_changed'] = 'stance_changed'

code class-attribute instance-attribute

code: str = 'encounter.stance.changed'

visibility class-attribute instance-attribute

stance instance-attribute

stance: str

SurpriseRolledEvent

Bases: Event

One side's surprise die (referee); roll is None when the side never rolls.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'encounter.surprise.rolled'})

event_type class-attribute instance-attribute

event_type: Literal['surprise_rolled'] = 'surprise_rolled'

code class-attribute instance-attribute

code: str = 'encounter.surprise.rolled'

visibility class-attribute instance-attribute

side instance-attribute

side: str

threshold instance-attribute

threshold: int

roll class-attribute instance-attribute

roll: int | None = None

surprised instance-attribute

surprised: bool

TimeAdvancedEvent

Bases: Event

The clock advanced (referee bookkeeping); rounds_total is the new position.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.time.advanced'})

event_type class-attribute instance-attribute

event_type: Literal['time_advanced'] = 'time_advanced'

code class-attribute instance-attribute

code: str = 'session.time.advanced'

visibility class-attribute instance-attribute

n instance-attribute

n: int

unit instance-attribute

unit: str

rounds_total instance-attribute

rounds_total: int

TrapEvent

Bases: Event

A trap outcome the party perceives.

.sprung when a trap goes off, .found when a search or inspection reveals one, .removed on a successful removal, .safe when a known trap's trigger resolved without springing — never emitted for unknown traps (the spring die rides the referee-visibility DetectionRolledEvent, no-leak).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset(
    {"exploration.trap.sprung", "exploration.trap.safe", "exploration.trap.found", "exploration.trap.removed"}
)

event_type class-attribute instance-attribute

event_type: Literal['trap'] = 'trap'

visibility class-attribute instance-attribute

trap_ref instance-attribute

trap_ref: str

character_id class-attribute instance-attribute

character_id: str | None = None

TreasureSoldEvent

Bases: Event

Valuables sold in town at full value (the 1-gp-1-XP identity kept clean).

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'town.treasure.sold'})

event_type class-attribute instance-attribute

event_type: Literal['treasure_sold'] = 'treasure_sold'

code class-attribute instance-attribute

code: str = 'town.treasure.sold'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

instance_ids instance-attribute

instance_ids: tuple[str, ...]

gp_value instance-attribute

gp_value: int

WanderingCheckEvent

Bases: Event

A wandering-monster check fired (referee bookkeeping).

roll is None when the clamped chance was 0 and the roll was skipped.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'exploration.wandering.checked'})

event_type class-attribute instance-attribute

event_type: Literal['wandering_check'] = 'wandering_check'

code class-attribute instance-attribute

code: str = 'exploration.wandering.checked'

visibility class-attribute instance-attribute

chance instance-attribute

chance: int

roll class-attribute instance-attribute

roll: int | None = None

encounter instance-attribute

encounter: bool

XpAwardedEvent

Bases: Event

An XP award applied to one character.

allowed_codes class-attribute

allowed_codes: frozenset[str] = frozenset({'session.xp.awarded'})

event_type class-attribute instance-attribute

event_type: Literal['xp_awarded'] = 'xp_awarded'

code class-attribute instance-attribute

code: str = 'session.xp.awarded'

visibility class-attribute instance-attribute

character_id instance-attribute

character_id: str

award instance-attribute

award: int

modified_award instance-attribute

modified_award: int

level_after instance-attribute

level_after: int

parse_any_event

parse_any_event(data: Mapping[str, object]) -> Event | None

Parse one serialized event, kernel or crawl, skipping unknown event types.

The session log's parser: an event_type this library doesn't know returns None instead of raising, so a newer producer's log loads under an older consumer (the session preserves the raw record).

Parameters:

Name Type Description Default
data Mapping[str, object]

A mapping previously produced by an event's model_dump.

required

Returns:

Type Description
Event | None

The event, or None when its event_type is unknown.

Raises:

Type Description
ContentValidationError

If the event type is known but the payload is malformed.