temporalio/temporal · error
chasm.Snapshot() called on child node: %+v
Error message
chasm.Snapshot() called on child node: %+v
What it means
Node.Snapshot() produces a full NodesSnapshot of a CHASM component tree and, per its contract, must only be invoked on the root node (a nil parent) when the tree is not dirty. The method soft-asserts n.parent == nil and panics with the node details when called on a child node. This is a programming-error guard: calling Snapshot on a child would produce an incomplete or invalid snapshot.
Source
Thrown at chasm/tree.go:2514
n.pendingUserMetadata = make(map[any]*sdkpb.UserMetadata)
}
n.needsPointerResolution = false
// Reset per-node subtreeIsDirty on all nodes in the tree.
for _, node := range n.andAllChildren() {
node.subtreeIsDirty = false
}
}
// Snapshot returns all nodes in the tree that have been modified after the given min versioned transition.
// A nil exclusiveMinVT will be treated as the same as the zero versioned transition and returns all nodes in the tree.
// This method should only be invoked on root CHASM node when IsDirty() is false.
func (n *Node) Snapshot(
exclusiveMinVT *persistencespb.VersionedTransition,
) NodesSnapshot {
if !softassert.That(n.logger, n.parent == nil, "chasm.Snapshot() should only be called on the root node") {
panic(fmt.Sprintf("chasm.Snapshot() called on child node: %+v", n))
}
// TODO: add assertion on IsDirty() once implemented
nodes := make(map[string]*persistencespb.ChasmNode)
n.snapshotInternal(exclusiveMinVT, nodes)
return NodesSnapshot{
Nodes: nodes,
}
}
func (n *Node) snapshotInternal(
exclusiveMinVT *persistencespb.VersionedTransition,
nodes map[string]*persistencespb.ChasmNode,
) {
if n == nil {
returnView on GitHub (pinned to bde624efd1)
Solutions
- Call Snapshot only on the root node of the tree (e.g. the node held by the tree/execution, not child nodes)
- To capture subtree state, use the appropriate node accessor/serialization APIs instead of Snapshot
- Ensure the tree is clean (not dirty) before snapshotting, as the API expects
Example fix
// before childNode := ctx.Node() // nested component node childNode.Snapshot(nil) // panics // after rootNode := tree.Root() // root node only rootNode.Snapshot(nil)
Defensive patterns
Strategy: type-guard
Validate before calling
// only snapshot roots
if node.Parent() != nil {
return errors.New("cannot snapshot a child node")
} Type guard
func isRootNode(n *chasm.Node) bool { return n != nil && n.Parent() == nil } Try / catch
defer func() {
if r := recover(); r != nil {
if s, ok := r.(string); ok && strings.Contains(s, "called on child node") {
// fix call site to use the root node
return
}
panic(r)
}
}() Prevention
- Only call Snapshot on the root node handle from the tree/execution
- Use child-specific accessors for nested components instead of Snapshot
- Verify tree cleanliness before snapshotting
When it happens
Trigger: Calling chasm Node.Snapshot() on a node obtained via child traversal (n.parent != nil) instead of the root node — e.g. calling Snapshot on a node returned from a context/child accessor rather than the tree root.
Common situations: Application code grabbing a node handle for a nested component and calling Snapshot on it to serialize partial state; test code asserting on subtrees via Snapshot; misuse of internal APIs during framework extensions.
Related errors
- failed to encode chasm path on clean tree: %v
- registrable component validation error: CHASM search attribu
- registrable component validation error: CHASM search attribu
- registrable component validation error: search attribute ali
- registrable component validation error: search attribute fie
AI-assisted analysis of temporalio/temporal@bde624efd1 (2026-09-01).
Data as JSON: /api/errors/29c31489c5934889.
Report an issue: GitHub.