{"record":{"id":"29c31489c5934889","repo":"temporalio/temporal","slug":"chasm-snapshot-called-on-child-node-v","errorCode":null,"errorMessage":"chasm.Snapshot() called on child node: %+v","messagePattern":"chasm\\.Snapshot\\(\\) called on child node: %\\+v","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"chasm/tree.go","lineNumber":2514,"sourceCode":"\t\tn.pendingUserMetadata = make(map[any]*sdkpb.UserMetadata)\n\t}\n\n\tn.needsPointerResolution = false\n\n\t// Reset per-node subtreeIsDirty on all nodes in the tree.\n\tfor _, node := range n.andAllChildren() {\n\t\tnode.subtreeIsDirty = false\n\t}\n}\n\n// Snapshot returns all nodes in the tree that have been modified after the given min versioned transition.\n// A nil exclusiveMinVT will be treated as the same as the zero versioned transition and returns all nodes in the tree.\n// This method should only be invoked on root CHASM node when IsDirty() is false.\nfunc (n *Node) Snapshot(\n\texclusiveMinVT *persistencespb.VersionedTransition,\n) NodesSnapshot {\n\tif !softassert.That(n.logger, n.parent == nil, \"chasm.Snapshot() should only be called on the root node\") {\n\t\tpanic(fmt.Sprintf(\"chasm.Snapshot() called on child node: %+v\", n))\n\t}\n\n\t// TODO: add assertion on IsDirty() once implemented\n\n\tnodes := make(map[string]*persistencespb.ChasmNode)\n\tn.snapshotInternal(exclusiveMinVT, nodes)\n\n\treturn NodesSnapshot{\n\t\tNodes: nodes,\n\t}\n}\n\nfunc (n *Node) snapshotInternal(\n\texclusiveMinVT *persistencespb.VersionedTransition,\n\tnodes map[string]*persistencespb.ChasmNode,\n) {\n\tif n == nil {\n\t\treturn","sourceCodeStart":2496,"sourceCodeEnd":2532,"githubUrl":"https://github.com/temporalio/temporal/blob/bde624efd13fbd3843654058db6d9c716166318b/chasm/tree.go#L2496-L2532","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nchildNode := ctx.Node() // nested component node\nchildNode.Snapshot(nil) // panics\n// after\nrootNode := tree.Root() // root node only\nrootNode.Snapshot(nil)","handlingStrategy":"type-guard","validationCode":"// only snapshot roots\nif node.Parent() != nil {\n    return errors.New(\"cannot snapshot a child node\")\n}","typeGuard":"func isRootNode(n *chasm.Node) bool { return n != nil && n.Parent() == nil }","tryCatchPattern":"defer func() {\n    if r := recover(); r != nil {\n        if s, ok := r.(string); ok && strings.Contains(s, \"called on child node\") {\n            // fix call site to use the root node\n            return\n        }\n        panic(r)\n    }\n}()","preventionTips":["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"],"tags":["chasm","tree","panic","api-misuse"],"backgroundTag":"snapshot-on-child-node","analyzedSha":"bde624efd13fbd3843654058db6d9c716166318b","analyzedAt":"2026-09-01T07:18:39.080Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}