Skip to content

API Reference

Event Taxonomy

Export Type Description
Namespace Base class Namespace for nested commands and outcomes; exposes __namespace_name__, __reducers__. Names are unique per graph, not per process — see namespace scope
Command Base class Imperative intent; must be nested inside a Namespace, and may not itself be subclassed once declared (class Rush(Order.Place) is a TypeError). Auto-exposes .Outcomes — union of nested DomainEvents. A handle method on the class registers as the command's inline handler when passed to EventGraph
DomainEvent Base class Fact inside the domain; must be nested inside a Namespace or Command
IntegrationEvent Base class Cross-boundary fact; top-level
SystemEvent Base class Framework-emitted fact; top-level
InvariantViolated Event Emitted when an invariants= predicate returns false

Mixins

Export Type Description
Auditable Mixin Marker for auto-logged events; trail() returns a compact summary. Compose with any event branch
MessageEvent Mixin Wraps LangChain BaseMessage; declares message or messages field. Compose with any event branch
SystemPromptSet Event Built-in IntegrationEvent + MessageEvent for system prompts
Event Internal Base class (not user-facing). Valid as type annotation and reducer event_type= catch-all

Handler Subscription

@on subscribes a handler. Three shapes:

  • Bare @on — event type inferred from the first parameter's annotation.
  • @on(kwargs=...) — inferred, with modifiers.
  • @on(Type, ...) — explicit; required for multi-event subscription.

Modifiers:

  • *field_matchers — field dispatch; type values do isinstance, str values do equality.
  • raises= — declared exceptions.
  • retry=RetryPolicy(...) — declarative backoff around the handler call. Spelled as a retry class attribute on a Command, alongside raises/invariants.
  • invariants={InvariantClass: predicate} — preconditions.
  • node_name= — pin a stable node identity (defaults to the function name) so renaming the function never breaks an interrupted checkpoint. previously= (str or tuple) — historic node names to keep resumable after a rename. These, and retry=, are reserved (a field named node_name/previously/retry can't be matched via **field_matchers; a Command declaring an annotated retry field also fails at class creation — use retry: ClassVar = ... for the policy, or rename the field). Inline Command.handle() handlers default their node identity to the command's __qualname__ (e.g. Order.Place), not the method name, so it is stable regardless of handlers=[...] order; a renamed command declares its historic node names as a class attribute, previously: ClassVar = (...), mirroring raises/invariants in spelling. All four are read off the command class that declares them — a concrete Command may not be subclassed, so a historic node name still identifies exactly one class. See Handler renames.

Returns enforced against the declared annotation, or the subscribed Command.Outcomes when unannotated. Omitting the return annotation is legal. An annotation that does not resolve at run time is not, and raises TypeError at graph construction — see Concepts › Return contract.

Graph & Execution

Export Type Description
EventGraph Class Build and run the event-driven graph; accepts @on-decorated functions and/or Command subclasses with inline handle. services=[...] (type-keyed) or services={...} (name-keyed) injects project dependencies into handler params (see Concepts › Signature injection). Type-keyed matching needs an annotation that resolves at run time; name-keyed matching needs no annotation, but an annotation that is present is checked against the registered value at graph build. on_unresumable="raise"\|"halt"\|"warn" (default "raise") governs resume() on a thread not awaiting input
RunScoped Class Marker for a service derived per run. Place RunScoped(factory) in the name-keyed services={...} mapping. The factory is called with the node's RunnableConfig once per node call. Its result is injected under the handler's parameter name. The factory must have a return annotation. The framework checks it against the handler parameter at graph build (see Concepts › Run-scoped services)
EventGraph.from_namespaces() Classmethod Build a graph from domains' inline command handlers; handlers= appends external handlers. With checkpointer=MemorySaver() auto-wires a NamespaceAwareSerde scoped to the passed namespaces and auto-collects every @migrate_from / @backfill decorator on those classes. Opt out by passing MemorySaver(serde=<custom>) — a user-supplied serde always wins
EventGraph.invoke() / .ainvoke() Method Run (sync/async); returns EventLog
EventGraph.resume() / .aresume() Method Resume an interrupted graph (requires checkpointer)
EventGraph.abandon() / .aabandon() Method Settles a paused thread without answering its interrupt. require_interrupt=True (default) raises ValueError if the thread has no pending interrupt. Pass False to settle it anyway, recording discarded="". Requires a checkpointer. Raises ValueError if the thread has no events to settle (never run, or only pre_seed()ed). Returns None. See Ending a pause without answering it
EventGraph.threads_paused_on() / .athreads_paused_on() Method Configs for each listed thread whose latest checkpoint has a pending interrupt, optionally filtered to an Interrupted class or subclass. thread_ids= is required: the caller names the threads, and the library does not walk the store. Get the ids from a server-side query. With event_type=None, returns every listed thread that is paused. thread_ids=None or a bare str raises TypeError. Requires a checkpointer. See Finding candidates server-side and Ending a pause without answering it
EventGraph.unrevivable_threads() / .aunrevivable_threads() Method Mapping of thread id to the qualnames its latest checkpoint can no longer revive, wherever the serde met them: settled history, pending interrupt, completed sibling write, or a field nested in a live event. Empty when every listed thread revives. Reads the store, not the baseline, so it sees a settled thread the coverage gates cannot. thread_ids= is required, the same as for threads_paused_on(). To sweep every thread, get the ids from one SELECT DISTINCT thread_id FROM checkpoints WHERE checkpoint_ns = '' query. If one checkpointer stores more than one graph, keep only the thread ids of this graph. Thread ids keep the caller's order, without duplicates. Requires a checkpointer with a NamespaceAwareSerde, raises ValueError otherwise. See Retiring an Interrupted subclass
EventGraph.plan_rewrite() / .aplan_rewrite() Method RewriteReport of the rewrite each listed thread needs, after the same verification as rewrite_store(). Writes nothing. drop= names event classes whose stored instances leave the log. thread_ids= is required, the same as for threads_paused_on(). To sweep every thread, get the ids from one SELECT DISTINCT thread_id FROM checkpoints WHERE checkpoint_ns = '' query. If one checkpointer stores more than one graph, keep only the thread ids of this graph. A listed id with no checkpoint is reported as refused. Requires a checkpointer with a NamespaceAwareSerde and legacy_write=False, raises ValueError otherwise. See Rewriting the live set
EventGraph.rewrite_store() / .arewrite_store() Method Rewrites the latest checkpoint of each listed thread under the live migration table, through the checkpointer's put() under the same checkpoint id, and drops the stored instances of each drop= class from events and _pending. It filters causes at the same positions and remaps each source. A cause whose source was dropped becomes SourceDropped(via, source_type). Same arguments and report as plan_rewrite(). A refused thread is not written; its reason says what to do. Run while the graph is idle, never inside a rolling deploy window, after a store-level backup. See Rewriting the live set
EventGraph.get_state() Method GraphState for a checkpointed thread
EventGraph.namespaces() Method Code-derived snapshot — domains, commands, outcomes, handlers, policies, edges, seeds. Returns a NamespaceModel
NamespaceModel.text(view=...) Method Human-readable tree; view="structure" or "choreography" (default)
NamespaceModel.mermaid(namespace_order=..., reactor_hub_min=..., focus=..., show_raises=..., notes=..., muted=...) Method Mermaid graph LR choreography diagram (handlers, policies, invariants, edges). namespace_order is "affinity" (default — cluster heavily-connected namespaces adjacent) or "alphabetical" (legacy, byte-stable for snapshot-pinned consumers). reactor_hub_min=N opts in to hub-style fanout: any (source, handler) producing ≥N solid-or-scatter targets renders as Source → Hub → {targets} with the handler name on the hub instead of repeated on every edge. focus=NamespaceModel.Focus(...) draws the selected items, every edge that touches one, the nodes at both ends dimmed as context, and every other edge between two drawn nodes; a selected reaction's solid edges are thick arrows, and a focused diagram draws no entry arrows. show_raises=False hides the (raises) edges. notes={key: text} adds lines under a label, and muted=[key, ...] fades what it names: a key is an event qualname, a reducer name, or a reaction name (its edges), so every Cause.via is a valid key. A wrong key raises ValueError that lists the valid keys. For a structure-only view use text(view="structure").
NamespaceModel.json() / .to_dict() Method JSON-serializable snapshot (event classes encoded as qualnames). Top-level schema_version is bumped on field removal, rename, or semantic change; additions don't bump.
NamespaceModel.{Namespace, Command, CommandHandler, Policy, Edge, Invariant} Nested dataclasses Frozen dataclasses for programmatic access
NamespaceModel.Edge Nested dataclass source, via, target, kind (how produced: solid/scatter/raises/retry/framework), causation (causal role: intent/react/orchestrate/chain/None)
NamespaceModel.reducers Field Tuple of NamespaceModel.Reducer — every reducer with name, subscribes and namespace. subscribes holds the concrete events of the model that the reducer folds, resolved from event_type (unlike Policy.subscribes, which holds the declared types), or None when a runtime_checkable Protocol with data members makes the folds unknown. namespace is a name, or None for a reducer passed to EventGraph(reducers=...)
NamespaceModel.Focus Nested dataclass The part of the graph that mermaid(focus=...) draws. Fields namespaces, reactions, reducers each take one name or an iterable of names, stored sorted. At least one name is required: an empty Focus() raises ValueError. Valid names are list(model.namespaces), [r.name for r in model.reactions] and [r.name for r in model.reducers]
NamespaceModel.invariants Field Tuple of NamespaceModel.Invariant — every declared invariant with cls, commands, declared_by, reactors
EventGraph.compiled Property Underlying CompiledStateGraph escape hatch
EventGraph.reducer_names Property frozenset of registered reducer names
EventGraph.reflect(log) Method Deterministic query surface over a run — returns a Reflection (see Reflection)
Reflection Class Facts-only read-model: context(), tool(), overview(), event(i), evidence(i), schema(), state(), .log. Injectable into handlers by parameter annotation, like EventLog
QueryTool Frozen dataclass The query_log LLM tool: name / description / parameters (JSON Schema) / run(...) -> str. Maps 1:1 to Anthropic / LangChain tool shapes
EventLog Class Immutable query container (see Concepts). cause(e), effects(e) and flow(e) read the causes that the log records. causes is the tuple of causes aligned with events, or None when the log records no causes. EventLog(events, causes=...) builds a log with causes, and EventLog(log.events, causes=log.causes) rebuilds a root log. A log from after(), before() or select() answers like its root
Cause Frozen dataclass (source, via): the event that a handler received, and the handler's graph node name. Event stores call source the causation ID. Cause.source is an event, while Edge.source is a type. via equals Edge.via unless the handler has a stable identity: an inline command handler (via is the command qualname), or an @on(node_name=...) pin. via must be a str. Returned by EventLog.cause()
UnknownCause Class Base of every case where the cause of an event is unknown. reason states why in one sentence. A cause is unknown only for history that the framework did not record
NotRecorded Frozen dataclass UnknownCause: the event was written before causes existed, in a checkpoint saved by an older release
SourceDropped Frozen dataclass UnknownCause (via, source_type): a handler produced the event, but rewrite_store(drop=...) deleted its source. The handler and the qualname of the deleted event stay known
FrameworkEvent Frozen dataclass The framework wrote the event: RunPaused, MaxRoundsExceeded, Cancelled or Abandoned. EventLog.cause() returns None only for a seed
GraphState NamedTuple (events, is_interrupted, interrupted). events carries the recorded causes of the thread
RewriteReport Dataclass (applied, threads) from plan_rewrite() / rewrite_store(). applied is False from the plan. .refused is the tuple of refused threads. str(report) prints one summary line, then one line per refused thread
ThreadRewrite Dataclass One thread's line in a RewriteReport: thread_id, status ("rewrite", "unchanged" or "refused"), migrated as (stored, live) identity pairs, dropped counts per identity, and reason on a refusal

System Events

Export Type Description
Halted Event Signal immediate termination; subclass for domain-specific halts
MaxRoundsExceeded Event Halted subtype when max_rounds is exceeded
Cancelled Event Halted subtype when an async handler is cancelled
Abandoned Event Halted subtype emitted by EventGraph.abandon() or .aabandon(). .reason is the caller-supplied reason. .discarded holds the discarded interrupts' qualnames (e.g. Order.ApprovalRequested, not the bare ApprovalRequested). The qualname stays unambiguous under nesting and stable whether or not the class still imports. Values are deduped and joined with ", " ("" if none). Match with in or .split(", "), never ==. Recorded, not dispatched: @on(Abandoned) never fires
Unresumable Event Halted subtype emitted by EventGraph(on_unresumable="halt") when resume() hits a thread not awaiting input. .resume_value is the resume event's type name. See Handler renames. For a deliberate settle instead of this runtime net, see abandon()
RunPaused Event SystemEvent (not Halted) emitted by the router when a per-call deadline=monotonic()+budget expires between dispatch rounds. Emitted at most once per /run regardless of how many parallel handlers fan in past the deadline. Cursor is advanced past it so a fresh /run on the same thread_id continues normally. No default AG-UI wire mapping — register a custom EventMapper to surface it on the wire
Interrupted Base class Subclass with typed fields to pause for human input. For frontend-discriminated payloads see InterruptedWithPayload in langgraph_events.agui
Resumed Event Emitted on resume() with the dispatched event + interrupted backref
HandlerRaised Event Emitted when a handler raises a raises=-declared exception; carries handler, source_event, exception, abandoned_for_deadline. The exception keeps its traceback — this is the terminal failure, the one you debug from. With a retry= policy it fires only after the budget is spent, or — uniquely tagged abandoned_for_deadline=True — once the next backoff would land on or past the run's deadline=. The flag is False on every other raise
HandlerRetried Event Emitted before each retry= backoff wait; carries handler, source_event, exception, attempt (1-based, the call that failed), delay_seconds. The exception instance is live (matchers and field injection work) but its __traceback__ is detached — as are those of its __cause__/__context__ chain and any ExceptionGroup members — so a breadcrumb never pins the failed attempt's frame; see Retries. A deadline give-up emits none, since no wait happened. Suppress with RetryPolicy(observe="log"\|"silent")
RetryPolicy Class Declarative retry for a handler call: max_attempts (counts the initial call), base_delay, max_delay, strategy ("exponential"/"constant"), jitter (full jitter, default True), on= (must overlap raises=, in either direction), respect_retry_after, observe. Distinct from langgraph.types.RetryPolicy, which re-runs a whole node
Invariant Marker class Subclass to declare a typed invariant; used as a dict key in invariants= and as the matcher value in @on(InvariantViolated, invariant=...)
InvariantViolated Event Emitted when an invariants= predicate returns false; carries invariant (instance of the declared Invariant subclass), handler, source_event. Use @on(InvariantViolated, invariant=SomeInvariant) to pin to a specific invariant
Scatter Class Fan-out into multiple events. Return type must enumerate concrete events (Scatter[WorkItem] or Scatter[A \| B]); bare Scatter, Scatter[Any], Scatter[Event], and Scatter[T] (TypeVar) are rejected at graph construction with TypeError

Reducers

Export Type Description
Reducer Class List accumulator; declare as Namespace class attribute or pass via reducers=
ScalarReducer Class Last-write-wins for a single value; None is valid
FoldReducer Class Folds each event into accumulating state via fold(self, state); next value depends on the prior. Generic over state type S
Foldable Protocol @runtime_checkable structural type for events with a fold method; types FoldReducer's event arg (don't inherit it)
BaseReducer Class Abstract base for custom reducers — subclass for bespoke channels
SKIP Sentinel Return from ScalarReducer.fn (or a FoldReducer.fold) to leave the value unchanged
RESET Sentinel Return from a FoldReducer.fold to clear the channel back to default_factory()
message_reducer Function Built-in reducer for MessageEvent projection

Lifecycle Hooks

Export Type Description
on_namespace_finalize(cls, callback) Function Schedule callback(cls, namespace_cls) to fire once cls's enclosing Namespace.__init_subclass__ finishes. Lets class decorators defer typing.get_type_hints() calls until forward references to siblings can resolve. Fires immediately if the enclosing Namespace has already finalized (e.g. when applied post-hoc to a bound class).

Streaming & Frames

Export Type Description
EventGraph.stream_events() / .astream_events() Method Yield events as produced; flags below control frame types
EventGraph.stream_resume() / .astream_resume() Method Streaming equivalent of resume()
emit_custom / aemit_custom Function Emit a custom stream event from a handler
emit_state_snapshot / aemit_state_snapshot Function Emit a typed state snapshot frame
STATE_SNAPSHOT_EVENT_NAME Constant Protocol name for snapshot events ("intermediate_state")

Stream frame types live in the langgraph_events.stream submodule:

from langgraph_events.stream import (
    StreamFrame,           # (event, reducers, changed_reducers) — include_reducers=True
    LLMToken, LLMStreamEnd,  # LLM token delta / completion — include_llm_tokens=True
    CustomEventFrame, StateSnapshotFrame,  # include_custom_events=True
)

Warnings

Export Type Description
OrphanedEventWarning Warning Issued at graph construction when a return type has no subscriber
UnresumableError RuntimeError subclass Raised by resume() (default EventGraph(on_unresumable="raise")) when the thread is not awaiting input — paused handler renamed/removed, thread already finished, double-resume, or a thread already settled by abandon(). Declare @on(previously=...) to recover, or set on_unresumable="halt"/"warn". See Handler renames
DomainPatternWarning Warning A namespace has 2+ events fanning out to an identical target set — a missing shared abstraction
CommandChainWarning Warning An inline Command.handle() emits another Command (a chain-causation edge) — prefer emitting a fact and reacting to it

Errors

Export Type Description
CommandPrivacyError TypeError subclass Raised at EventGraph construction; also at runtime when a handler with a broad base-class annotation (e.g. -> DomainEvent) constructs a Cmd.Private(...) that bypasses the static contract check. Outcomes nested inside a Command are private to that Command's inline handle() — neither sibling Commands nor non-inline reactors may produce them
ReducerNotSetError ValueError subclass Raised at injection time (before the handler body runs) when a handler parameter rejects None (e.g. strategy: str) and the channel value is None. Sits outside the raises= catch boundary — a broad raises=ValueError cannot swallow it. Opt out by widening the annotation to str | None, Any, object, or omitting it

AG-UI Subpackage

Requires [agui]. See AG-UI Adapter.

Export Type Description
AGUIAdapter Class Map EventGraph streams to AG-UI protocol events
AGUI_EXTRAS_KEY Constant Reserved BaseMessage.additional_kwargs key. Its mapping is forwarded as extra fields on the AG-UI message, and receives the client's extra fields on the way back
AGUIAdapter.stream() / .connect() / .reconnect() Method Execute / rehydrate checkpoint / refresh
FrontendStateMutated Event Adapter-emitted IntegrationEvent carrying RunAgentInput.state for client-state-mirror reducers
FrontendToolCallRequested Event Interrupted subclass for handler-initiated frontend tool calls (CopilotKit useFrontendTool)
InterruptedWithPayload[PayloadT] Generic base Typed-payload variant of Interrupted; subclasses implement interrupt_payload(self) -> PayloadT. The built-in InterruptedMapper recognizes it directly — no agui_dict() override needed
AGUICustomEvent / AGUISerializable Protocol Override fallback event names / payload serialization
SeedFactory / ResumeFactory / EventMapper Protocol Input → seed events; resume detection; event mapping
MapperContext Dataclass Shared state (run ID, thread ID, message counter) per stream
create_starlette_response / encode_sse_stream Function Framework-agnostic SSE wrapping
extract_resume_input Function Pulls and JSON-decodes RunAgentInput.forwarded_props["command"]["resume"]. Returns None if absent or falsy
agui_messages_to_langchain Function Converts AG-UI protocol messages to LangChain BaseMessage instances. Maps ToolMessage.error to status="error" and extra fields to additional_kwargs[AGUI_EXTRAS_KEY]. Optional drop_invalid_tool_calls=True to skip tool calls whose JSON arguments fail to parse
merge_frontend_messages Function resume_factory helper: reads messages from checkpoint_state["reducers"][reducer_name], converts input_data.messages, merges via add_messages (id-based dedup). Returns a tuple
build_langchain_tools / detect_new_tool_results Function Convert RunAgentInput.tools to OpenAI-format / extract new ToolMessages from a resume request

Serde Subpackage

Namespace-aware serialization, auto-wired by default. EventGraph.from_namespaces(..., checkpointer=MemorySaver()) builds a NamespaceAwareSerde scoped to the passed namespaces and auto-collects every @migrate_from / @backfill on those classes. Opt out by passing MemorySaver(serde=<custom>). See Event migrations.

Exported from langgraph_events.serde unless a different module is noted.

Export Type Description
NamespaceAwareSerde Class JsonPlusSerializer subclass keying Event identity by (__module__, __qualname__). Drop-in for any checkpointer accepting serde=. EventGraph.from_namespaces auto-wires one scoped to its namespaces when the checkpointer carries the default serde; pass your own via MemorySaver(serde=...) to opt out. Kwargs: migrations=, namespaces=, legacy_write=. legacy_write=True is refused at construction with an origin-scoped fill, any transform or any split
migrate_from Decorator @migrate_from(*old_qualnames, in_module=None, backfill=None, transform=None) — records historic identities a class held. backfill={field: default} scopes fills to payloads written under this decorator's qualname (one qualname per decorator) — the fan-in case where collapsed origins each pin their own discriminator. transform=fn rewrites the stored kwargs of payloads written under this decorator's qualname only, before the rename (one qualname per decorator). transform=lambda kw: {} retires an identity onto an empty tombstone. Auto-collected from the namespaces the serde was built with; stacks; not MRO-inherited
backfill Decorator @backfill(field, *, default=… \| default_factory=…) — class-global back-fill for a field that is required in code but absent from pre-existing payloads (one value for every origin and era; acts as the fallback under origin-scoped fills). Same auto-collection as @migrate_from; reuses the AddField mutable-default guard
transform_fields Decorator @transform_fields(fn) — the class stage: rewrites the stored kwargs for a field the class dropped, merged or retyped. fn receives a copy of the kwargs and returns the kwargs the constructor receives. Runs after any rename, on payloads from every era, so it must be idempotent. One per class. migrate_from(transform=fn) is the origin stage. Same auto-collection as @backfill. See Dropping, merging or retyping a field
split_event Decorator @split_event(select, *, targets=(...)) — applied to the source class, whose identity stays stored. select receives a copy of the stored kwargs and returns None to keep the source, or (target_class, kwargs) to build one of targets instead. Runs in the class stage, after any rename and @transform_fields, before the fills of the resulting identity. targets is validated at serde construction. One per class. Same auto-collection as @transform_fields. See Splitting one stored event into two on a payload value
Migration Class Hand-authored migration group for cross-module / composite cases. Migration.rename(...) / Migration.add_field(...) / Migration.transform_fields(...) / Migration.split_event(...) are single-op sugar; pass the live target as to=<class> / target=<class> / source=<class> (refactor-safe) instead of new_module/new_qualname strings. add_field or transform_fields keyed on a historic module/qualname is origin-scoped (applies before the rename) — the escape hatch for a per-origin default_factory or transform. split_event(source=Class, select=fn, targets=(...)) keys on the live source only
assert_all_baselined_cover Function assert_all_baselined_cover(serde, baseline_path) — weakest coverage gate: set-membership over revivable_identities(); raises MigrationCoverageError if an identity is neither live nor rename-covered. Walks the baseline's events and retired lists both. Scoped to the serde's namespaces= walk plus events=. See coverage gates
assert_all_baselined_resolve Function assert_all_baselined_resolve(serde, baseline_path) — rename-aware gate: asserts every baselined identity resolves to a live Event class without constructing it. Walks events and retired both. For baselines with __post_init__-validated, framework, or module-level events
assert_all_baselined_revive Function assert_all_baselined_revive(serde, baseline_path) — strongest gate: pushes a synthesized payload for every baselined identity through the real read path and asserts it revives. Walks events and retired both. A v3 baseline's recorded fields are sent too, so a field the live class dropped fails here. No per-event list to maintain
synthesize_legacy_payload Function synthesize_legacy_payload(module, qualname, kwargs) — builds the (format, bytes) a prior release would have written. For pinning a specific drifted field shape; the loop gate above covers identity reachability
replay_reducer Function replay_reducer(reducer, events) — rebuild a reducer's channel value from its (already-migrated) event log when the projection/output shape changed. Thin wrapper over BaseReducer.seed
NamespaceAwareSerde.revivable_identities Method Read-only frozenset of every revivable (module, qualname). For custom coverage rules; assert_all_baselined_cover is the gate
assert_all_baselined_handlers_cover Function assert_all_baselined_handlers_cover(graph, baseline_path) — handler analog: asserts every baselined handler node name is still live or covered by an @on(previously=...) alias. Takes the EventGraph. See Handler renames
assert_resume_recovers Function assert_resume_recovers(before, after, *, seed, resume_with, thread_id=None) — behavioral recovery proof: invokes before to interrupt, resumes after on the same checkpoint, asserts a Resumed (real recovery). The handler analog of assert_all_baselined_revive; both graphs must share one checkpointer. See Testing handler recovery
CoverageError Exception AssertionError base for both coverage failures; except CoverageError catches event and handler gates alike
MigrationCoverageError Exception Raised by the event gates (assert_all_baselined_cover/_resolve/_revive); CoverageError subclass. .uncovered is the offending (module, qualname) tuple. .retired is the subset the baseline lists as retired
HandlerCoverageError Exception Raised by assert_all_baselined_handlers_cover; CoverageError subclass (sibling of MigrationCoverageError). .uncovered is the offending handler node-name tuple
write_baseline Function (…serde.migrations.detect) write_baseline(graph, path) — snapshots topology into a v3 baseline: every identity with its cumulative fields, the handler names, and a retired list of identities the graph no longer reaches. Never erases an identity. The allow_removed= keyword is deprecated and does nothing. See When to commit the baseline
detect_changes Function (…serde.migrations.detect) detect_changes(graph, baseline_path) — diffs topology vs baseline into rename/ambiguous/removed buckets. Also runnable as the CI gate python -m langgraph_events.serde.migrations <module:factory> <baseline>
BaselineRegressionError Exception (…serde.migrations.detect) Retained for one release; write_baseline no longer raises it. ValueError subclass. .removed is the tuple of dropped identities

Raw RenameEvent / AddField / TransformFields / SplitEvent operation constructors are the composite escape hatch in langgraph_events.serde.migrations — intentionally not re-exported at the top level (the decorator + Migration sugar is the public tier). TransformFields(module, qualname, transform) rewrites the stored kwargs of one identity; the identity picks the stage, the same rule as AddField. SplitEvent(module, qualname, select, targets) keys on a live identity and builds one of targets when select returns it; the class stage only. Wrap either like every raw op: Migration(operations=(TransformFields(module, qualname, transform),)).