Repository: yorkie-team/yorkie
Pull request #570 (2023-07-03 06:49:41)

Remove panic from code
<!--  Thanks for sending a pull request! -->

**What this PR does / why we need it**:
- Remove panic from crdt/tree, index/tree, element-rht and let them return error when it happens
- let json/... handle error
**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Related to #497

**Special notes for your reviewer**:

**Does this PR introduce a user-facing change?**:
<!--
If no, just write "NONE" in the release-note block below.
If yes, a release note is required:
Enter your extended release note in the block below. If the PR requires additional action from users switching to the new release, include the string "action required".
-->
```release-note

```

**Additional documentation**:

<!--
This section can be blank if this pull request does not require a release note.

Please use the following format for linking documentation:
- [Usage]: <link>
- [Other doc]: <link>
-->
```docs

```

**Checklist**:
- [x] Added relevant tests or not required
- [x] Didn't break anything


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) Report
> Merging [#570](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) (1dda134) into [main](https://app.codecov.io/gh/yorkie-team/yorkie/commit/aa2f3618f8fc0a53cfdca6717b37a6dfa1eeadd0?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) (aa2f361) will **decrease** coverage by `0.64%`.
> The diff coverage is `28.69%`.

> :exclamation: Current head 1dda134 differs from pull request most recent head ded2576. Consider uploading reports for the commit ded2576 to get more accurate results

```diff
@@            Coverage Diff             @@
##             main     #570      +/-   ##
==========================================
- Coverage   51.91%   51.28%   -0.64%     
==========================================
  Files          67       67              
  Lines        6851     6959     +108     
==========================================
+ Hits         3557     3569      +12     
- Misses       2855     2920      +65     
- Partials      439      470      +31     
```


| [Impacted Files](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) | Coverage Δ | |
|---|---|---|
| [api/converter/from\_pb.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-YXBpL2NvbnZlcnRlci9mcm9tX3BiLmdv) | `48.94% <0.00%> (-0.29%)` | :arrow_down: |
| [pkg/document/crdt/text.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvdGV4dC5nbw==) | `57.84% <ø> (ø)` | |
| [pkg/document/document.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2RvY3VtZW50Lmdv) | `45.56% <0.00%> (-1.87%)` | :arrow_down: |
| [pkg/index/tree.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2luZGV4L3RyZWUuZ28=) | `47.61% <24.13%> (-2.56%)` | :arrow_down: |
| [pkg/document/crdt/tree.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvdHJlZS5nbw==) | `52.72% <29.56%> (-11.67%)` | :arrow_down: |
| [pkg/document/crdt/root.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvcm9vdC5nbw==) | `70.49% <33.33%> (-5.38%)` | :arrow_down: |
| [pkg/document/crdt/element\_rht.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvZWxlbWVudF9yaHQuZ28=) | `62.85% <50.00%> (+0.53%)` | :arrow_up: |
| [pkg/document/crdt/rga\_tree\_list.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvcmdhX3RyZWVfbGlzdC5nbw==) | `62.69% <50.00%> (+0.29%)` | :arrow_up: |
| [pkg/document/internal\_document.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2ludGVybmFsX2RvY3VtZW50Lmdv) | `22.58% <50.00%> (+0.54%)` | :arrow_up: |
| [pkg/document/crdt/array.go](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team#diff-cGtnL2RvY3VtZW50L2NyZHQvYXJyYXkuZ28=) | `32.07% <100.00%> (ø)` | |
| ... and [2 more](https://app.codecov.io/gh/yorkie-team/yorkie/pull/570?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=yorkie-team) | |



Patch:
diff --git a/api/converter/from_pb.go b/api/converter/from_pb.go
index 651a373c..0e12ca10 100644
--- a/api/converter/from_pb.go
+++ b/api/converter/from_pb.go
@@ -638,7 +638,9 @@ func FromTreeNodes(pbNodes []*api.TreeNode) (*crdt.TreeNode, error) {
 			}
 		}
 
-		parent.Prepend(nodes[i])
+		if err := parent.Prepend(nodes[i]); err != nil {
+			return nil, err
+		}
 	}
 
 	// build crdt.Tree from root to construct the links between nodes.
diff --git a/pkg/document/crdt/array.go b/pkg/document/crdt/array.go
index 02dd57ab..9faf4441 100644
--- a/pkg/document/crdt/array.go
+++ b/pkg/document/crdt/array.go
@@ -38,8 +38,8 @@ func NewArray(elements *RGATreeList, createdAt *time.Ticket) *Array {
 }
 
 // Purge physically purge child element.
-func (a *Array) Purge(elem Element) {
-	a.elements.purge(elem)
+func (a *Array) Purge(elem Element) error {
+	return a.elements.purge(elem)
 }
 
 // Add adds the given element at the last.
diff --git a/pkg/document/crdt/element.go b/pkg/document/crdt/element.go
index c7c7ee22..2702d2de 100644
--- a/pkg/document/crdt/element.go
+++ b/pkg/document/crdt/element.go
@@ -17,15 +17,20 @@
 package crdt
 
 import (
+	"errors"
+
 	"github.com/yorkie-team/yorkie/pkg/document/time"
 )
 
+// ErrChildNotFound is returned when the child is not found in the container.
+var ErrChildNotFound = errors.New("child not found")
+
 // Container represents Array or Object.
 type Container interface {
 	Element
 
 	// Purge physically purges the given child element.
-	Purge(child Element)
+	Purge(child Element) error
 
 	// Descendants returns all descendants of this container.
 	Descendants(callback func(elem Element, parent Container) bool)
@@ -38,7 +43,7 @@ type Container interface {
 type GCElement interface {
 	Element
 	removedNodesLen() int
-	purgeRemovedNodesBefore(ticket *time.Ticket) int
+	purgeRemovedNodesBefore(ticket *time.Ticket) (int, error)
 }
 
 // Element represents JSON element.
diff --git a/pkg/document/crdt/element_rht.go b/pkg/document/crdt/element_rht.go
index 9c0faac5..5d90699e 100644
--- a/pkg/document/crdt/element_rht.go
+++ b/pkg/document/crdt/element_rht.go
@@ -167,10 +167,10 @@ func (rht *ElementRHT) Nodes() []*ElementRHTNode {
 }
 
 // purge physically purge child element.
-func (rht *ElementRHT) purge(elem Element) {
+func (rht *ElementRHT) purge(elem Element) error {
 	node, ok := rht.nodeMapByCreatedAt[elem.CreatedAt().Key()]
 	if !ok {
-		panic("fail to find: " + elem.CreatedAt().Key())
+		return fmt.Errorf("purge %s: %w", elem.CreatedAt().Key(), ErrChildNotFound)
 	}
 	delete(rht.nodeMapByCreatedAt, node.elem.CreatedAt().Key())
 
@@ -178,6 +178,8 @@ func (rht *ElementRHT) purge(elem Element) {
 	if ok && node == nodeByKey {
 		delete(rht.nodeMapByKey, nodeByKey.key)
 	}
+
+	return nil
 }
 
 // Marshal returns the JSON encoding of this map.
diff --git a/pkg/document/crdt/object.go b/pkg/document/crdt/object.go
index 2793261d..c27d43d3 100644
--- a/pkg/document/crdt/object.go
+++ b/pkg/document/crdt/object.go
@@ -38,8 +38,8 @@ func NewObject(memberNodes *ElementRHT, createdAt *time.Ticket) *Object {
 }
 
 // Purge physically purge child element.
-func (o *Object) Purge(elem Element) {
-	o.memberNodes.purge(elem)
+func (o *Object) Purge(elem Element) error {
+	return o.memberNodes.purge(elem)
 }
 
 // Set sets the given element of the given key.
diff --git a/pkg/document/crdt/rga_tree_list.go b/pkg/document/crdt/rga_tree_list.go
index 6830bf2b..0273b4c6 100644
--- a/pkg/document/crdt/rga_tree_list.go
+++ b/pkg/document/crdt/rga_tree_list.go
@@ -17,6 +17,7 @@
 package crdt
 
 import (
+	"fmt"
 	"strings"
 
 	"github.com/yorkie-team/yorkie/pkg/document/time"
@@ -278,13 +279,15 @@ func (a *RGATreeList) FindPrevCreatedAt(createdAt *time.Ticket) *time.Ticket {
 }
 
 // purge physically purge child element.
-func (a *RGATreeList) purge(elem Element) {
+func (a *RGATreeList) purge(elem Element) error {
 	node, ok := a.nodeMapByCreatedAt[elem.CreatedAt().Key()]
 	if !ok {
-		panic("fail to find the given createdAt: " + elem.CreatedAt().Key())
+		return fmt.Errorf("purge %s: %w", elem.CreatedAt().Key(), ErrChildNotFound)
 	}
 
 	a.release(node)
+
+	return nil
 }
 
 func (a *RGATreeList) findNextBeforeExecutedAt(
diff --git a/pkg/document/crdt/rga_tree_split.go b/pkg/document/crdt/rga_tree_split.go
index 5c4b9ee0..640e0a33 100644
--- a/pkg/document/crdt/rga_tree_split.go
+++ b/pkg/document/crdt/rga_tree_split.go
@@ -632,7 +632,7 @@ func (s *RGATreeSplit[V]) removedNodesLen() int {
 }
 
 // purgeRemovedNodesBefore physically purges nodes that have been removed.
-func (s *RGATreeSplit[V]) purgeRemovedNodesBefore(ticket *time.Ticket) int {
+func (s *RGATreeSplit[V]) purgeRemovedNodesBefore(ticket *time.Ticket) (int, error) {
 	count := 0
 	for _, node := range s.removedNodeMap {
 		if node.removedAt != nil && ticket.Compare(node.removedAt) >= 0 {
@@ -644,7 +644,7 @@ func (s *RGATreeSplit[V]) purgeRemovedNodesBefore(ticket *time.Ticket) int {
 		}
 	}
 
-	return count
+	return count, nil
 }
 
 // purge physically purge the given node from RGATreeSplit.
diff --git a/pkg/document/crdt/root.go b/pkg/document/crdt/root.go
index 9b4c860c..5fbe8fe7 100644
--- a/pkg/document/crdt/root.go
+++ b/pkg/document/crdt/root.go
@@ -110,25 +110,32 @@ func (r *Root) DeepCopy() (*Root, error) {
 }
 
 // GarbageCollect purge elements that were removed before the given time.
-func (r *Root) GarbageCollect(ticket *time.Ticket) int {
+func (r *Root) GarbageCollect(ticket *time.Ticket) (int, error) {
 	count := 0
 
 	for _, pair := range r.removedElementPairMapByCreatedAt {
 		if pair.elem.RemovedAt() != nil && ticket.Compare(pair.elem.RemovedAt()) >= 0 {
-			pair.parent.Purge(pair.elem)
+			if err := pair.parent.Purge(pair.elem); err != nil {
+				return 0, err
+			}
+
 			count += r.garbageCollect(pair.elem)
 		}
 	}
 
 	for _, node := range r.elementHasRemovedNodesSetByCreatedAt {
-		purgedNodes := node.purgeRemovedNodesBefore(ticket)
+		purgedNodes, err := node.purgeRemovedNodesBefore(ticket)
+		if err != nil {
+			return 0, err
+		}
+
 		if purgedNodes > 0 {
 			delete(r.elementHasRemovedNodesSetByCreatedAt, node.CreatedAt().Key())
 		}
 		count += purgedNodes
 	}
 
-	return count
+	return count, nil
 }
 
 // ElementMapLen returns the size of element map.
diff --git a/pkg/document/crdt/text.go b/pkg/document/crdt/text.go
index c6378c31..45caab38 100644
--- a/pkg/document/crdt/text.go
+++ b/pkg/document/crdt/text.go
@@ -310,6 +310,6 @@ func (t *Text) removedNodesLen() int {
 }
 
 // purgeRemovedNodesBefore physically purges nodes that have been removed.
-func (t *Text) purgeRemovedNodesBefore(ticket *time.Ticket) int {
+func (t *Text) purgeRemovedNodesBefore(ticket *time.Ticket) (int, error) {
 	return t.rgaTreeSplit.purgeRemovedNodesBefore(ticket)
 }
diff --git a/pkg/document/crdt/tree.go b/pkg/document/crdt/tree.go
index de3b1945..7559cb5b 100644
--- a/pkg/document/crdt/tree.go
+++ b/pkg/document/crdt/tree.go
@@ -28,6 +28,11 @@ import (
 	"github.com/yorkie-team/yorkie/pkg/llrb"
 )
 
+var (
+	// ErrNodeNotFound is returned when the node is not found.
+	ErrNodeNotFound = errors.New("node not found")
+)
+
 var (
 	// DummyTreePos is a dummy position of Tree. It is used to represent the head node of RGASplit.
 	DummyTreePos = &TreePos{
@@ -151,43 +156,48 @@ func (n *TreeNode) Attributes() string {
 }
 
 // Append appends the given node to the end of the children.
-func (n *TreeNode) Append(newNodes ...*TreeNode) {
+func (n *TreeNode) Append(newNodes ...*TreeNode) error {
 	indexNodes := make([]*index.Node[*TreeNode], len(newNodes))
 	for i, newNode := range newNodes {
 		indexNodes[i] = newNode.IndexTreeNode
 	}
 
-	n.IndexTreeNode.Append(indexNodes...)
+	return n.IndexTreeNode.Append(indexNodes...)
 }
 
 // Prepend prepends the given node to the beginning of the children.
-func (n *TreeNode) Prepend(newNodes ...*TreeNode) {
+func (n *TreeNode) Prepend(newNodes ...*TreeNode) error {
 	indexNodes := make([]*index.Node[*TreeNode], len(newNodes))
 	for i, newNode := range newNodes {
 		indexNodes[i] = newNode.IndexTreeNode
 	}
 
-	n.IndexTreeNode.Prepend(indexNodes...)
+	return n.IndexTreeNode.Prepend(indexNodes...)
 }
 
 // Child returns the child of the given offset.
-func (n *TreeNode) Child(offset int) *TreeNode {
-	return n.IndexTreeNode.Child(offset).Value
+func (n *TreeNode) Child(offset int) (*TreeNode, error) {
+	child, err := n.IndexTreeNode.Child(offset)
+	if err != nil {
+		return nil, err
+	}
+
+	return child.Value, nil
 }
 
 // Split splits the node at the given offset.
-func (n *TreeNode) Split(offset int) *TreeNode {
+func (n *TreeNode) Split(offset int) (*TreeNode, error) {
 	if n.IsText() {
 		return n.SplitText(offset)
 	}
 
-	return nil
+	return nil, nil
 }
 
 // SplitText splits the text node at the given offset.
-func (n *TreeNode) SplitText(offset int) *TreeNode {
+func (n *TreeNode) SplitText(offset int) (*TreeNode, error) {
 	if offset == 0 || offset == n.Len() {
-		return nil
+		return nil, nil
 	}
 
 	encoded := utf16.Encode([]rune(n.Value))
@@ -201,9 +211,14 @@ func (n *TreeNode) SplitText(offset int) *TreeNode {
 		CreatedAt: n.Pos.CreatedAt,
 		Offset:    offset,
 	}, n.Type(), nil, string(rightRune))
-	n.IndexTreeNode.Parent.InsertAfterInternal(rightNode.IndexTreeNode, n.IndexTreeNode)
+	if err := n.IndexTreeNode.Parent.InsertAfterInternal(
+		rightNode.IndexTreeNode,
+		n.IndexTreeNode,
+	); err != nil {
+		return nil, err
+	}
 
-	return rightNode
+	return rightNode, nil
 }
 
 // remove marks the node as removed.
@@ -219,12 +234,12 @@ func (n *TreeNode) remove(removedAt *time.Ticket) {
 }
 
 // InsertAt inserts the given node at the given offset.
-func (n *TreeNode) InsertAt(newNode *TreeNode, offset int) {
-	n.IndexTreeNode.InsertAt(newNode.IndexTreeNode, offset)
+func (n *TreeNode) InsertAt(newNode *TreeNode, offset int) error {
+	return n.IndexTreeNode.InsertAt(newNode.IndexTreeNode, offset)
 }
 
 // DeepCopy copies itself deeply.
-func (n *TreeNode) DeepCopy() *TreeNode {
+func (n *TreeNode) DeepCopy() (*TreeNode, error) {
 	var clone *TreeNode
 	if n.Attrs != nil {
 		clone = NewTreeNode(n.Pos, n.Type(), n.Attrs.DeepCopy(), n.Value)
@@ -234,16 +249,23 @@ func (n *TreeNode) DeepCopy() *TreeNode {
 	clone.RemovedAt = n.RemovedAt
 
 	if n.IsText() {
-		return clone
+		return clone, nil
 	}
 
 	var children []*index.Node[*TreeNode]
 	for _, child := range n.IndexTreeNode.Children(true) {
-		node := child.Value.DeepCopy()
+		node, err := child.Value.DeepCopy()
+		if err != nil {
+			return nil, err
+		}
+
 		children = append(children, node.IndexTreeNode)
 	}
-	clone.IndexTreeNode.SetChildren(children)
-	return clone
+	if err := clone.IndexTreeNode.SetChildren(children); err != nil {
+		return nil, err
+	}
+
+	return clone, nil
 }
 
 // Tree represents the tree of CRDT. It has doubly linked list structure and
@@ -291,7 +313,7 @@ func (t *Tree) removedNodesLen() int {
 }
 
 // purgeRemovedNodesBefore physically purges nodes that have been removed.
-func (t *Tree) purgeRemovedNodesBefore(ticket *time.Ticket) int {
+func (t *Tree) purgeRemovedNodesBefore(ticket *time.Ticket) (int, error) {
 	count := 0
 	nodesToBeRemoved := make(map[*TreeNode]bool)
 
@@ -302,22 +324,25 @@ func (t *Tree) purgeRemovedNodesBefore(ticket *time.Ticket) int {
 		}
 	}
 
-	index.TraverseAll(t.IndexTree, func(node *index.Node[*TreeNode], depth int) {
-		_, ok := nodesToBeRemoved[node.Value]
-
-		if ok {
+	if err := index.TraverseAll(t.IndexTree, func(node *index.Node[*TreeNode], depth int) error {
+		if _, ok := nodesToBeRemoved[node.Value]; ok {
 			parent := node.Parent
 
 			if parent == nil {
 				count--
 				delete(nodesToBeRemoved, node.Value)
-
-				return
+				return nil
 			}
 
-			parent.RemoveChild(node)
+			if err := parent.RemoveChild(node); err != nil {
+				return err
+			}
 		}
-	})
+
+		return nil
+	}); err != nil {
+		return 0, err
+	}
 
 	for node := range nodesToBeRemoved {
 		t.NodeMapByPos.Remove(node.Pos)
@@ -325,7 +350,7 @@ func (t *Tree) purgeRemovedNodesBefore(ticket *time.Ticket) int {
 		delete(t.removedNodeMap, node.Pos.CreatedAt.StructureAsString()+":"+strconv.Itoa(node.Pos.Offset))
 	}
 
-	return count
+	return count, nil
 }
 
 // Purge physically purges the given node.
@@ -369,7 +394,12 @@ func marshal(builder *strings.Builder, node *TreeNode) {
 
 // DeepCopy copies itself deeply.
 func (t *Tree) DeepCopy() (Element, error) {
-	return NewTree(t.Root().DeepCopy(), t.createdAt), nil
+	node, err := t.Root().DeepCopy()
+	if err != nil {
+		return nil, err
+	}
+
+	return NewTree(node, t.createdAt), nil
 }
 
 // CreatedAt returns the creation time of this Tree.
@@ -443,38 +473,55 @@ func (t *Tree) ToXML() string {
 
 // EditByIndex edits the given range with the given value.
 // This method uses indexes instead of a pair of TreePos for testing.
-func (t *Tree) EditByIndex(start, end int, content *TreeNode, editedAt *time.Ticket) {
-	fromPos := t.FindPos(start)
-	toPos := t.FindPos(end)
+func (t *Tree) EditByIndex(start, end int, content *TreeNode, editedAt *time.Ticket) error {
+	fromPos, err := t.FindPos(start)
+	if err != nil {
+		return err
+	}
+	toPos, err := t.FindPos(end)
+	if err != nil {
+		return err
+	}
 
-	t.Edit(fromPos, toPos, content, editedAt)
+	return t.Edit(fromPos, toPos, content, editedAt)
 }
 
 // FindPos finds the position of the given index in the tree.
-func (t *Tree) FindPos(offset int) *TreePos {
-	treePos := t.IndexTree.FindTreePos(offset)
+func (t *Tree) FindPos(offset int) (*TreePos, error) {
+	treePos, err := t.IndexTree.FindTreePos(offset)
+	if err != nil {
+		return nil, err
+	}
 
 	return &TreePos{
 		CreatedAt: treePos.Node.Value.Pos.CreatedAt,
 		Offset:    treePos.Node.Value.Pos.Offset + treePos.Offset,
-	}
+	}, nil
 }
 
 // Edit edits the tree with the given range and content.
 // If the content is undefined, the range will be removed.
-func (t *Tree) Edit(from, to *TreePos, content *TreeNode, editedAt *time.Ticket) {
+func (t *Tree) Edit(from, to *TreePos, content *TreeNode, editedAt *time.Ticket) error {
 	// 01. split text nodes at the given range if needed.
-	toPos, toRight := t.findTreePosWithSplitText(to, editedAt)
-	fromPos, fromRight := t.findTreePosWithSplitText(from, editedAt)
+	toPos, toRight, err := t.findTreePosWithSplitText(to, editedAt)
+	if err != nil {
+		return err
+	}
+	fromPos, fromRight, err := t.findTreePosWithSplitText(from, editedAt)
+	if err != nil {
+		return err
+	}
 
 	toBeRemoveds := make([]*TreeNode, 0)
 	// 02. remove the nodes and update linked list and index tree.
 	if fromRight != toRight {
-		t.nodesBetween(fromRight, toRight, func(node *TreeNode) {
+		if err := t.nodesBetween(fromRight, toRight, func(node *TreeNode) {
 			if !node.IsRemoved() {
 				toBeRemoveds = append(toBeRemoveds, node)
 			}
-		})
+		}); err != nil {
+			return err
+		}
 
 		isRangeOnSameBranch := toPos.Node.IsAncestorOf(fromPos.Node)
 		for _, node := range toBeRemoveds {
@@ -498,15 +545,23 @@ func (t *Tree) Edit(from, to *TreePos, content *TreeNode, editedAt *time.Ticket)
 			// insert the alive children of the removed block node to the toNode.
 			if removedBlockNode != nil {
 				blockNode := toPos.Node
-				offset := blockNode.FindBranchOffset(removedBlockNode.IndexTreeNode)
+				offset, err := blockNode.FindBranchOffset(removedBlockNode.IndexTreeNode)
+				if err != nil {
+					return err
+				}
+
 				for i := len(removedBlockNode.IndexTreeNode.Children()) - 1; i >= 0; i-- {
 					node := removedBlockNode.IndexTreeNode.Children()[i]
-					blockNode.InsertAt(node, offset)
+					if err := blockNode.InsertAt(node, offset); err != nil {
+						return err
+					}
 				}
 			}
 		} else {
 			if fromPos.Node.Parent != nil && fromPos.Node.Parent.Value.IsRemoved() {
-				toPos.Node.Parent.Prepend(fromPos.Node.Parent.Children()...)
+				if err := toPos.Node.Parent.Prepend(fromPos.Node.Parent.Children()...); err != nil {
+					return err
+				}
 			}
 		}
 	}
@@ -523,33 +578,53 @@ func (t *Tree) Edit(from, to *TreePos, content *TreeNode, editedAt *time.Ticket)
 		// 03-2. insert the content nodes to the tree.
 		if fromPos.Node.IsText() {
 			if fromPos.Offset == 0 {
-				fromPos.Node.Parent.InsertBefore(content.IndexTreeNode, fromPos.Node)
+				if err := fromPos.Node.Parent.InsertBefore(content.IndexTreeNode, fromPos.Node); err != nil {
+					return err
+				}
 			} else {
-				fromPos.Node.Parent.InsertAfter(content.IndexTreeNode, fromPos.Node)
+				if err := fromPos.Node.Parent.InsertAfter(content.IndexTreeNode, fromPos.Node); err != nil {
+					return err
+				}
 			}
 		} else {
 			target := fromPos.Node
-			target.InsertAt(content.IndexTreeNode, fromPos.Offset+1)
+			if err := target.InsertAt(content.IndexTreeNode, fromPos.Offset+1); err != nil {
+				return err
+			}
 		}
 	}
+
+	return nil
 }
 
 // StyleByIndex applies the given attributes of the given range.
 // This method uses indexes instead of a pair of TreePos for testing.
-func (t *Tree) StyleByIndex(start, end int, attributes map[string]string, editedAt *time.Ticket) {
-	fromPos := t.FindPos(start)
-	toPos := t.FindPos(end)
+func (t *Tree) StyleByIndex(start, end int, attributes map[string]string, editedAt *time.Ticket) error {
+	fromPos, err := t.FindPos(start)
+	if err != nil {
+		return err
+	}
+	toPos, err := t.FindPos(end)
+	if err != nil {
+		return err
+	}
 
-	t.Style(fromPos, toPos, attributes, editedAt)
+	return t.Style(fromPos, toPos, attributes, editedAt)
 }
 
 // Style applies the given attributes of the given range.
-func (t *Tree) Style(from, to *TreePos, attributes map[string]string, editedAt *time.Ticket) {
-	_, toRight := t.findTreePos(to, editedAt)
-	_, fromRight := t.findTreePos(from, editedAt)
+func (t *Tree) Style(from, to *TreePos, attributes map[string]string, editedAt *time.Ticket) error {
+	_, toRight, err := t.findTreePos(to, editedAt)
+	if err != nil {
+		return err
+	}
+	_, fromRight, err := t.findTreePos(from, editedAt)
+	if err != nil {
+		return err
+	}
 
 	// 02. style the nodes.
-	t.nodesBetween(fromRight, toRight, func(node *TreeNode) {
+	return t.nodesBetween(fromRight, toRight, func(node *TreeNode) {
 		if node.IsText() {
 			return
 		}
@@ -564,10 +639,10 @@ func (t *Tree) Style(from, to *TreePos, attributes map[string]string, editedAt *
 }
 
 // findTreePos returns TreePos and the right node of the given index in postorder.
-func (t *Tree) findTreePos(pos *TreePos, editedAt *time.Ticket) (*index.TreePos[*TreeNode], *TreeNode) {
+func (t *Tree) findTreePos(pos *TreePos, editedAt *time.Ticket) (*index.TreePos[*TreeNode], *TreeNode, error) {
 	treePos := t.toTreePos(pos)
 	if treePos == nil {
-		panic(fmt.Errorf("cannot find node at %p", pos))
+		return nil, nil, fmt.Errorf("%p: %w", pos, ErrNodeNotFound)
 	}
 
 	// Find the appropriate position. This logic is similar to the logical to
@@ -583,15 +658,21 @@ func (t *Tree) findTreePos(pos *TreePos, editedAt *time.Ticket) (*index.TreePos[
 	}
 
 	// TODO(hackerwins): Consider to use current instead of treePos.
-	right := t.IndexTree.FindPostorderRight(treePos)
-	return current, right
+	right, err := t.IndexTree.FindPostorderRight(treePos)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return current, right, nil
 }
 
 // findTreePosWithSplitText finds the right node of the given index in postorder.
-func (t *Tree) findTreePosWithSplitText(pos *TreePos, editedAt *time.Ticket) (*index.TreePos[*TreeNode], *TreeNode) {
+func (t *Tree) findTreePosWithSplitText(pos *TreePos, editedAt *time.Ticket) (
+	*index.TreePos[*TreeNode], *TreeNode, error,
+) {
 	treePos := t.toTreePos(pos)
 	if treePos == nil {
-		panic(fmt.Errorf("cannot find node at %p", pos))
+		return nil, nil, fmt.Errorf("%p: %w", pos, ErrNodeNotFound)
 	}
 
 	// Find the appropriate position. This logic is similar to the logical to
@@ -607,15 +688,23 @@ func (t *Tree) findTreePosWithSplitText(pos *TreePos, editedAt *time.Ticket) (*i
 	}
 
 	if current.Node.IsText() {
-		split := current.Node.Value.Split(current.Offset)
+		split, err := current.Node.Value.Split(current.Offset)
+		if err != nil {
+			return nil, nil, err
+		}
+
 		if split != nil {
 			t.InsertAfter(current.Node.Value, split)
 			split.InsPrev = current.Node.Value
 		}
 	}
 
-	right := t.IndexTree.FindPostorderRight(treePos)
-	return current, right
+	right, err := t.IndexTree.FindPostorderRight(treePos)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	return current, right, nil
 }
 
 // toTreePos converts the given crdt.TreePos to index.TreePos<CRDTTreeNode>.
@@ -637,27 +726,34 @@ func (t *Tree) toTreePos(pos *TreePos) *index.TreePos[*TreeNode] {
 }
 
 // toIndex converts the given CRDTTreePos to the index of the tree.
-func (t *Tree) toIndex(pos *TreePos) int {
+func (t *Tree) toIndex(pos *TreePos) (int, error) {
 	treePos := t.toTreePos(pos)
 	if treePos == nil {
-		return -1
+		return -1, nil
 	}
 
-	return t.IndexTree.IndexOf(treePos.Node) + treePos.Offset
+	idx, err := t.IndexTree.IndexOf(treePos.Node)
+	if err != nil {
+		return 0, err
+	}
+
+	return idx + treePos.Offset, nil
 }
 
 // nodesBetween returns the nodes between the given range.
 // This method includes the given left node but excludes the given right node.
-func (t *Tree) nodesBetween(left *TreeNode, right *TreeNode, callback func(*TreeNode)) {
+func (t *Tree) nodesBetween(left *TreeNode, right *TreeNode, callback func(*TreeNode)) error {
 	current := left
 	for current != right {
 		if current == nil {
-			panic(errors.New("left and right are not in the same list"))
+			return errors.New("left and right are not in the same list")
 		}
 
 		callback(current)
 		current = current.Next
 	}
+
+	return nil
 }
 
 // Structure returns the structure of this tree.
@@ -666,13 +762,16 @@ func (t *Tree) Structure() TreeNodeForTest {
 }
 
 // PathToPos returns the position of the given path
-func (t *Tree) PathToPos(path []int) *TreePos {
-	treePos := t.IndexTree.PathToTreePos(path)
+func (t *Tree) PathToPos(path []int) (*TreePos, error) {
+	treePos, err := t.IndexTree.PathToTreePos(path)
+	if err != nil {
+		return nil, err
+	}
 
 	return &TreePos{
 		CreatedAt: treePos.Node.Value.Pos.CreatedAt,
 		Offset:    treePos.Node.Value.Pos.Offset + treePos.Offset,
-	}
+	}, nil
 }
 
 // ToStructure returns the JSON of this tree for debugging.
diff --git a/pkg/document/document.go b/pkg/document/document.go
index 821a9126..5c98d5b0 100644
--- a/pkg/document/document.go
+++ b/pkg/document/document.go
@@ -209,9 +209,17 @@ func (d *Document) Root() *json.Object {
 // GarbageCollect purge elements that were removed before the given time.
 func (d *Document) GarbageCollect(ticket *time.Ticket) int {
 	if d.clone != nil {
-		d.clone.GarbageCollect(ticket)
+		if _, err := d.clone.GarbageCollect(ticket); err != nil {
+			panic(err)
+		}
 	}
-	return d.doc.GarbageCollect(ticket)
+
+	n, err := d.doc.GarbageCollect(ticket)
+	if err != nil {
+		panic(err)
+	}
+
+	return n
 }
 
 // GarbageLen returns the count of removed elements.
diff --git a/pkg/document/internal_document.go b/pkg/document/internal_document.go
index 1a0b7ca8..9163fc1f 100644
--- a/pkg/document/internal_document.go
+++ b/pkg/document/internal_document.go
@@ -133,14 +133,16 @@ func (d *InternalDocument) ApplyChangePack(pack *change.Pack) error {
 	d.checkpoint = d.checkpoint.Forward(pack.Checkpoint)
 
 	if pack.MinSyncedTicket != nil {
-		d.GarbageCollect(pack.MinSyncedTicket)
+		if _, err := d.GarbageCollect(pack.MinSyncedTicket); err != nil {
+			return err
+		}
 	}
 
 	return nil
 }
 
 // GarbageCollect purge elements that were removed before the given time.
-func (d *InternalDocument) GarbageCollect(ticket *time.Ticket) int {
+func (d *InternalDocument) GarbageCollect(ticket *time.Ticket) (int, error) {
 	return d.root.GarbageCollect(ticket)
 }
 
diff --git a/pkg/document/json/tree.go b/pkg/document/json/tree.go
index 96ecc206..dc7710bc 100644
--- a/pkg/document/json/tree.go
+++ b/pkg/document/json/tree.go
@@ -80,19 +80,33 @@ func (t *Tree) Edit(fromIdx, toIdx int, content *TreeNode) bool {
 		}
 		node = crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), content.Type, attributes, content.Value)
 		for _, child := range content.Children {
-			buildDescendants(t.context, child, node)
+			if err := buildDescendants(t.context, child, node); err != nil {
+				panic(err)
+			}
 		}
 	}
 
-	fromPos := t.Tree.FindPos(fromIdx)
-	toPos := t.Tree.FindPos(toIdx)
+	fromPos, err := t.Tree.FindPos(fromIdx)
+	if err != nil {
+		panic(err)
+	}
+	toPos, err := t.Tree.FindPos(toIdx)
+	if err != nil {
+		panic(err)
+	}
+
 	var clone *crdt.TreeNode
 	if node != nil {
-		clone = node.DeepCopy()
+		clone, err = node.DeepCopy()
+		if err != nil {
+			panic(err)
+		}
 	}
 
 	ticket = t.context.LastTimeTicket()
-	t.Tree.Edit(fromPos, toPos, clone, ticket)
+	if err = t.Tree.Edit(fromPos, toPos, clone, ticket); err != nil {
+		panic(err)
+	}
 
 	t.context.Push(operations.NewTreeEdit(
 		t.CreatedAt(),
@@ -129,19 +143,33 @@ func (t *Tree) EditByPath(fromPath []int, toPath []int, content *TreeNode) bool
 		}
 		node = crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), content.Type, attributes, content.Value)
 		for _, child := range content.Children {
-			buildDescendants(t.context, child, node)
+			if err := buildDescendants(t.context, child, node); err != nil {
+				panic(err)
+			}
 		}
 	}
 
-	fromPos := t.Tree.PathToPos(fromPath)
-	toPos := t.Tree.PathToPos(toPath)
+	fromPos, err := t.Tree.PathToPos(fromPath)
+	if err != nil {
+		panic(err)
+	}
+	toPos, err := t.Tree.PathToPos(toPath)
+	if err != nil {
+		panic(err)
+	}
+
 	var clone *crdt.TreeNode
 	if node != nil {
-		clone = node.DeepCopy()
+		clone, err = node.DeepCopy()
+		if err != nil {
+			panic(err)
+		}
 	}
 
 	ticket = t.context.LastTimeTicket()
-	t.Tree.Edit(fromPos, toPos, clone, ticket)
+	if err = t.Tree.Edit(fromPos, toPos, clone, ticket); err != nil {
+		panic(err)
+	}
 
 	t.context.Push(operations.NewTreeEdit(
 		t.CreatedAt(),
@@ -164,11 +192,19 @@ func (t *Tree) Style(fromIdx, toIdx int, attributes map[string]string) bool {
 		panic("from should be less than or equal to to")
 	}
 
-	fromPos := t.Tree.FindPos(fromIdx)
-	toPos := t.Tree.FindPos(toIdx)
+	fromPos, err := t.Tree.FindPos(fromIdx)
+	if err != nil {
+		panic(err)
+	}
+	toPos, err := t.Tree.FindPos(toIdx)
+	if err != nil {
+		panic(err)
+	}
 
 	ticket := t.context.IssueTimeTicket()
-	t.Tree.Style(fromPos, toPos, attributes, ticket)
+	if err := t.Tree.Style(fromPos, toPos, attributes, ticket); err != nil {
+		panic(err)
+	}
 
 	t.context.Push(operations.NewTreeStyle(
 		t.CreatedAt(),
@@ -190,18 +226,19 @@ func buildRoot(ctx *change.Context, node *TreeNode, createdAt *time.Ticket) *crd
 
 	root := crdt.NewTreeNode(crdt.NewTreePos(createdAt, 0), node.Type, nil)
 	for _, child := range node.Children {
-		buildDescendants(ctx, child, root)
+		if err := buildDescendants(ctx, child, root); err != nil {
+			panic(err)
+		}
 	}
 
 	return root
 }
 
 // buildDescendants converts the given node to a CRDT-based tree node.
-func buildDescendants(ctx *change.Context, n TreeNode, parent *crdt.TreeNode) {
+func buildDescendants(ctx *change.Context, n TreeNode, parent *crdt.TreeNode) error {
 	if n.Type == index.DefaultTextType {
 		treeNode := crdt.NewTreeNode(crdt.NewTreePos(ctx.IssueTimeTicket(), 0), n.Type, nil, n.Value)
-		parent.Append(treeNode)
-		return
+		return parent.Append(treeNode)
 	}
 
 	ticket := ctx.IssueTimeTicket()
@@ -215,9 +252,15 @@ func buildDescendants(ctx *change.Context, n TreeNode, parent *crdt.TreeNode) {
 	}
 
 	treeNode := crdt.NewTreeNode(crdt.NewTreePos(ticket, 0), n.Type, attributes)
-	parent.Append(treeNode)
+	if err := parent.Append(treeNode); err != nil {
+		return err
+	}
 
 	for _, child := range n.Children {
-		buildDescendants(ctx, child, treeNode)
+		if err := buildDescendants(ctx, child, treeNode); err != nil {
+			return err
+		}
 	}
+
+	return nil
 }
diff --git a/pkg/document/operations/tree_edit.go b/pkg/document/operations/tree_edit.go
index d9d38af8..aeb4e8c6 100644
--- a/pkg/document/operations/tree_edit.go
+++ b/pkg/document/operations/tree_edit.go
@@ -64,10 +64,16 @@ func (e *TreeEdit) Execute(root *crdt.Root) error {
 	switch obj := parent.(type) {
 	case *crdt.Tree:
 		var content *crdt.TreeNode
+		var err error
 		if e.Content() != nil {
-			content = e.Content().DeepCopy()
+			content, err = e.Content().DeepCopy()
+			if err != nil {
+				return err
+			}
+		}
+		if err = obj.Edit(e.from, e.to, content, e.executedAt); err != nil {
+			return err
 		}
-		obj.Edit(e.from, e.to, content, e.executedAt)
 
 		if e.from.CreatedAt.Compare(e.to.CreatedAt) != 0 || e.from.Offset != e.to.Offset {
 			root.RegisterElementHasRemovedNodes(obj)
diff --git a/pkg/document/operations/tree_style.go b/pkg/document/operations/tree_style.go
index f0ed76d8..2024a37b 100644
--- a/pkg/document/operations/tree_style.go
+++ b/pkg/document/operations/tree_style.go
@@ -64,8 +64,7 @@ func (e *TreeStyle) Execute(root *crdt.Root) error {
 		return ErrNotApplicableDataType
 	}
 
-	obj.Style(e.from, e.to, e.attributes, e.executedAt)
-	return nil
+	return obj.Style(e.from, e.to, e.attributes, e.executedAt)
 }
 
 // FromPos returns the start point of the editing range.
diff --git a/pkg/index/tree.go b/pkg/index/tree.go
index c482a499..d0b29ab6 100644
--- a/pkg/index/tree.go
+++ b/pkg/index/tree.go
@@ -64,6 +64,20 @@ import (
  * Index 1 can be converted to TreePos(i, 0).
  */
 
+var (
+	// ErrInvalidMethodCallForTextNode is returned when a invalid method is called for text node.
+	ErrInvalidMethodCallForTextNode = errors.New("text node cannot have children")
+
+	// ErrChildNotFound is returned when a child is not found.
+	ErrChildNotFound = errors.New("child not found")
+
+	// ErrUnreachablePath is returned when a path is unreachable.
+	ErrUnreachablePath = errors.New("unreachable path")
+
+	// ErrInvalidTreePos is returned when a TreePos is invalid.
+	ErrInvalidTreePos = errors.New("invalid tree pos")
+)
+
 const (
 	// DefaultTextType is the type of default text node.
 	// TODO(hackerwins): Allow users to define the type of text node.
@@ -94,35 +108,38 @@ func postorderTraversal[V Value](node *Node[V], callback func(node *Node[V], dep
 }
 
 // postorderTraversalAll traverses the whole tree (include tombstones) with postorder traversal.
-func postorderTraversalAll[V Value](node *Node[V], callback func(node *Node[V], depth int), depth int) {
+func postorderTraversalAll[V Value](node *Node[V], callback func(node *Node[V], depth int) error, depth int) error {
 	if node == nil {
-		return
+		return nil
 	}
 
 	for _, child := range node.children {
-		postorderTraversalAll(child, callback, depth+1)
+		if err := postorderTraversalAll(child, callback, depth+1); err != nil {
+			return err
+		}
 	}
-	callback(node, depth)
+
+	return callback(node, depth)
 }
 
 // nodesBetween iterates the nodes between the given range.
 // If the given range is collapsed, the callback is not called.
 // It traverses the tree with postorder traversal.
-func nodesBetween[V Value](root *Node[V], from, to int, callback func(node V)) {
+func nodesBetween[V Value](root *Node[V], from, to int, callback func(node V)) error {
 	if from > to {
-		panic(fmt.Sprintf("from cannot be greater than to %d > %d", from, to))
+		return fmt.Errorf("from cannot be greater than to %d > %d", from, to)
 	}
 
 	if from > root.Length {
-		panic(fmt.Sprintf("from is out of range %d > %d", from, root.Length))
+		return fmt.Errorf("from is out of range %d > %d", from, root.Length)
 	}
 
 	if to > root.Length {
-		panic(fmt.Sprintf("to is out of range %d > %d", to, root.Length))
+		return fmt.Errorf("to is out of range %d > %d", to, root.Length)
 	}
 
 	if from == to {
-		return
+		return nil
 	}
 
 	pos := 0
@@ -136,12 +153,14 @@ func nodesBetween[V Value](root *Node[V], from, to int, callback func(node V)) {
 			if child.IsText() {
 				toChild = to - pos
 			}
-			nodesBetween(
+			if err := nodesBetween(
 				child,
 				int(math.Max(0, float64(fromChild))),
 				int(math.Min(float64(toChild), float64(child.Length))),
 				callback,
-			)
+			); err != nil {
+				return err
+			}
 
 			if fromChild < 0 || toChild > child.Length || child.IsText() {
 				callback(child.Value)
@@ -149,6 +168,8 @@ func nodesBetween[V Value](root *Node[V], from, to int, callback func(node V)) {
 		}
 		pos += child.PaddedLength()
 	}
+
+	return nil
 }
 
 // ToXML returns the XML representation of this tree.
@@ -173,8 +194,8 @@ func Traverse[V Value](tree *Tree[V], callback func(node *Node[V], depth int)) {
 }
 
 // TraverseAll traverses the whole tree (include tombstones) with postorder traversal.
-func TraverseAll[V Value](tree *Tree[V], callback func(node *Node[V], depth int)) {
-	postorderTraversalAll(tree.root, callback, 0)
+func TraverseAll[V Value](tree *Tree[V], callback func(node *Node[V], depth int) error) error {
+	return postorderTraversalAll(tree.root, callback, 0)
 }
 
 // Value represents the data stored in the nodes of Tree.
@@ -219,9 +240,9 @@ func (n *Node[V]) IsText() bool {
 }
 
 // Append appends the given node to the end of the children.
-func (n *Node[V]) Append(newNodes ...*Node[V]) {
+func (n *Node[V]) Append(newNodes ...*Node[V]) error {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return ErrInvalidMethodCallForTextNode
 	}
 
 	n.children = append(n.children, newNodes...)
@@ -229,6 +250,8 @@ func (n *Node[V]) Append(newNodes ...*Node[V]) {
 		newNode.Parent = n
 		newNode.UpdateAncestorsSize()
 	}
+
+	return nil
 }
 
 // Children returns the children of the given node.
@@ -251,9 +274,9 @@ func (n *Node[V]) Children(includeRemovedNode ...bool) []*Node[V] {
 }
 
 // SetChildren sets the children of the given node.
-func (n *Node[V]) SetChildren(children []*Node[V]) {
+func (n *Node[V]) SetChildren(children []*Node[V]) error {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return ErrInvalidMethodCallForTextNode
 	}
 
 	n.children = children
@@ -261,6 +284,8 @@ func (n *Node[V]) SetChildren(children []*Node[V]) {
 		child.Parent = n
 		child.UpdateAncestorsSize()
 	}
+
+	return nil
 }
 
 // UpdateAncestorsSize updates the size of ancestors.
@@ -289,62 +314,67 @@ func (n *Node[V]) PaddedLength() int {
 }
 
 // Child returns the child of the given index.
-func (n *Node[V]) Child(index int) *Node[V] {
+func (n *Node[V]) Child(index int) (*Node[V], error) {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return nil, ErrInvalidMethodCallForTextNode
 	}
 
-	return n.Children()[index]
+	return n.Children()[index], nil
 }
 
 // InsertAfterInternal inserts the given node after the given child.
 // This method does not update the size of the ancestors.
-func (n *Node[V]) InsertAfterInternal(newNode, prevNode *Node[V]) {
+func (n *Node[V]) InsertAfterInternal(newNode, prevNode *Node[V]) error {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return ErrInvalidMethodCallForTextNode
 	}
 
 	offset := n.OffsetOfChild(prevNode)
 	if offset == -1 {
-		panic(errors.New("prevNode is not a child of the node"))
+		return errors.New("prevNode is not a child of the node")
 	}
 
 	// TODO(hackerwins, krapie): Needs to inspect this code later
 	n.children = append(n.children[:offset+1], n.children[offset:]...)
 	n.children[offset+1] = newNode
 	newNode.Parent = n
+
+	return nil
 }
 
 // nextSibling returns the next sibling of the node.
-func (n *Node[V]) nextSibling() *Node[V] {
-	offset := n.Parent.findOffset(n)
+func (n *Node[V]) nextSibling() (*Node[V], error) {
+	offset, err := n.Parent.findOffset(n)
+	if err != nil {
+		return nil, err
+	}
 
 	// TODO(hackerwins): Needs to inspect the code below later.
 	// if the node is the last child, there is no next sibling.
 	if len(n.Parent.Children()) <= offset+1 {
-		return nil
+		return nil, nil
 	}
 	sibling := n.Parent.Children()[offset+1]
 	if sibling != nil {
-		return sibling
+		return sibling, nil
 	}
 
-	return nil
+	return nil, nil
 }
 
 // findOffset returns the offset of the given node in the children.
-func (n *Node[V]) findOffset(node *Node[V]) int {
+func (n *Node[V]) findOffset(node *Node[V]) (int, error) {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return 0, ErrInvalidMethodCallForTextNode
 	}
 
 	for i, child := range n.Children() {
 		if child == node {
-			return i
+			return i, nil
 		}
 	}
 
-	return -1
+	return -1, nil
 }
 
 // IsAncestorOf returns true if the node is an ancestor of the given node.
@@ -371,39 +401,44 @@ func (n *Node[V]) ancestorOf(ancestor, node *Node[V]) bool {
 
 // FindBranchOffset returns offset of the given descendant node in this node.
 // If the given node is not a descendant of this node, it returns -1.
-func (n *Node[V]) FindBranchOffset(node *Node[V]) int {
+func (n *Node[V]) FindBranchOffset(node *Node[V]) (int, error) {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return 0, ErrInvalidMethodCallForTextNode
 	}
 
 	current := node
 	for current != nil {
 		offset := n.OffsetOfChild(current)
 		if offset != -1 {
-			return offset
+			return offset, nil
 		}
 
 		current = current.Parent
 	}
 
-	return -1
+	return -1, nil
 }
 
 // InsertAt inserts the given node at the given offset.
-func (n *Node[V]) InsertAt(newNode *Node[V], offset int) {
+func (n *Node[V]) InsertAt(newNode *Node[V], offset int) error {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return ErrInvalidMethodCallForTextNode
+	}
+
+	if err := n.insertAtInternal(newNode, offset); err != nil {
+		return err
 	}
 
-	n.insertAtInternal(newNode, offset)
 	newNode.UpdateAncestorsSize()
+
+	return nil
 }
 
 // insertAtInternal inserts the given node at the given index.
 // This method does not update the size of the ancestors.
-func (n *Node[V]) insertAtInternal(newNode *Node[V], offset int) {
+func (n *Node[V]) insertAtInternal(newNode *Node[V], offset int) error {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return ErrInvalidMethodCallForTextNode
 	}
 
 	// splice the new node into the children
@@ -414,12 +449,14 @@ func (n *Node[V]) insertAtInternal(newNode *Node[V], offset int) {
 		n.children = append(n.children[:offset], append([]*Node[V]{newNode}, n.children[offset:]...)...)
 	}
 	newNode.Parent = n
+
+	return nil
 }
 
 // Prepend prepends the given nodes to the children.
-func (n *Node[V]) Prepend(children ...*Node[V]) {
+func (n *Node[V]) Prepend(children ...*Node[V]) error {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return ErrInvalidMethodCallForTextNode
 	}
 
 	n.children = append(children, n.children...)
@@ -427,12 +464,14 @@ func (n *Node[V]) Prepend(children ...*Node[V]) {
 		node.Parent = n
 		node.UpdateAncestorsSize()
 	}
+
+	return nil
 }
 
 // RemoveChild removes the given child.
-func (n *Node[V]) RemoveChild(child *Node[V]) {
+func (n *Node[V]) RemoveChild(child *Node[V]) error {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return ErrInvalidMethodCallForTextNode
 	}
 	offset := -1
 
@@ -444,41 +483,53 @@ func (n *Node[V]) RemoveChild(child *Node[V]) {
 	}
 
 	if offset == -1 {
-		panic(errors.New("child not found"))
+		return ErrChildNotFound
 	}
 
 	n.children = append(n.children[:offset], n.children[offset+1:]...)
 	child.Parent = nil
+
+	return nil
 }
 
 // InsertBefore inserts the given node before the given child.
-func (n *Node[V]) InsertBefore(newNode, referenceNode *Node[V]) {
+func (n *Node[V]) InsertBefore(newNode, referenceNode *Node[V]) error {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return ErrInvalidMethodCallForTextNode
 	}
 
 	offset := n.OffsetOfChild(referenceNode)
 	if offset == -1 {
-		panic(errors.New("child not found"))
+		return ErrChildNotFound
+	}
+
+	if err := n.insertAtInternal(newNode, offset); err != nil {
+		return err
 	}
 
-	n.insertAtInternal(newNode, offset)
 	newNode.UpdateAncestorsSize()
+
+	return nil
 }
 
 // InsertAfter inserts the given node after the given child.
-func (n *Node[V]) InsertAfter(newNode, referenceNode *Node[V]) {
+func (n *Node[V]) InsertAfter(newNode, referenceNode *Node[V]) error {
 	if n.IsText() {
-		panic(errors.New("text node cannot have children"))
+		return ErrInvalidMethodCallForTextNode
 	}
 
 	offset := n.OffsetOfChild(referenceNode)
 	if offset == -1 {
-		panic(errors.New("child not found"))
+		return ErrChildNotFound
+	}
+
+	if err := n.insertAtInternal(newNode, offset+1); err != nil {
+		return err
 	}
 
-	n.insertAtInternal(newNode, offset+1)
 	newNode.UpdateAncestorsSize()
+
+	return nil
 }
 
 // hasTextChild returns true if the node has a text child.
@@ -504,8 +555,8 @@ func (n *Node[V]) OffsetOfChild(node *Node[V]) int {
 }
 
 // NodesBetween returns the nodes between the given range.
-func (t *Tree[V]) NodesBetween(from int, to int, callback func(node V)) {
-	nodesBetween(t.root, from, to, callback)
+func (t *Tree[V]) NodesBetween(from int, to int, callback func(node V)) error {
+	return nodesBetween(t.root, from, to, callback)
 }
 
 // TreePos is the position of a node in the tree.
@@ -532,7 +583,7 @@ func (t *Tree[V]) Root() *Node[V] {
 }
 
 // FindTreePos finds the position of the given index in the tree.
-func (t *Tree[V]) FindTreePos(index int, preferTexts ...bool) *TreePos[V] {
+func (t *Tree[V]) FindTreePos(index int, preferTexts ...bool) (*TreePos[V], error) {
 	preferText := true
 	if len(preferTexts) > 0 {
 		preferText = preferTexts[0]
@@ -541,16 +592,16 @@ func (t *Tree[V]) FindTreePos(index int, preferTexts ...bool) *TreePos[V] {
 	return t.findTreePos(t.root, index, preferText)
 }
 
-func (t *Tree[V]) findTreePos(node *Node[V], index int, preferText bool) *TreePos[V] {
+func (t *Tree[V]) findTreePos(node *Node[V], index int, preferText bool) (*TreePos[V], error) {
 	if index > node.Length {
-		panic(fmt.Errorf("index is out of range: %d > %d", index, node.Length))
+		return nil, fmt.Errorf("index is out of range: %d > %d", index, node.Length)
 	}
 
 	if node.IsText() {
 		return &TreePos[V]{
 			Node:   node,
 			Offset: index,
-		}
+		}, nil
 	}
 
 	// offset is the index of the child node.
@@ -569,7 +620,7 @@ func (t *Tree[V]) findTreePos(node *Node[V], index int, preferText bool) *TreePo
 			return &TreePos[V]{
 				Node:   node,
 				Offset: offset,
-			}
+			}, nil
 		}
 
 		// The position is in right side of the element node and preferText is false.
@@ -577,7 +628,7 @@ func (t *Tree[V]) findTreePos(node *Node[V], index int, preferText bool) *TreePo
 			return &TreePos[V]{
 				Node:   node,
 				Offset: offset + 1,
-			}
+			}, nil
 		}
 
 		// The position is in middle the element node.
@@ -595,18 +646,18 @@ func (t *Tree[V]) findTreePos(node *Node[V], index int, preferText bool) *TreePo
 	return &TreePos[V]{
 		Node:   node,
 		Offset: offset,
-	}
+	}, nil
 }
 
 // TreePosToPath returns path from given treePos
-func (t *Tree[V]) TreePosToPath(treePos *TreePos[V]) []int {
+func (t *Tree[V]) TreePosToPath(treePos *TreePos[V]) ([]int, error) {
 	var path []int
 	node := treePos.Node
 
 	if node.IsText() {
 		offset := node.Parent.OffsetOfChild(node)
 		if offset == -1 {
-			panic("invalid treePos")
+			return nil, ErrInvalidTreePos
 		}
 
 		leftSiblingsSize := 0
@@ -630,7 +681,7 @@ func (t *Tree[V]) TreePosToPath(treePos *TreePos[V]) []int {
 		}
 
 		if ^pathInfo == 0 {
-			panic("invalid treePos")
+			return nil, ErrInvalidTreePos
 		}
 
 		path = append(path, pathInfo)
@@ -644,13 +695,13 @@ func (t *Tree[V]) TreePosToPath(treePos *TreePos[V]) []int {
 		reversePath[len(path)-i-1] = pathInfo
 	}
 
-	return reversePath
+	return reversePath, nil
 }
 
 // PathToTreePos returns treePos from given path
-func (t *Tree[V]) PathToTreePos(path []int) *TreePos[V] {
+func (t *Tree[V]) PathToTreePos(path []int) (*TreePos[V], error) {
 	if len(path) == 0 {
-		panic("unacceptable path")
+		return nil, ErrUnreachablePath
 	}
 
 	node := t.root
@@ -659,7 +710,7 @@ func (t *Tree[V]) PathToTreePos(path []int) *TreePos[V] {
 		node = node.Children()[pathElement]
 
 		if node == nil {
-			panic("unacceptable path")
+			return nil, ErrUnreachablePath
 		}
 	}
 
@@ -667,19 +718,19 @@ func (t *Tree[V]) PathToTreePos(path []int) *TreePos[V] {
 		return findTextPos(node, path[len(path)-1])
 	}
 	if len(node.Children()) < path[len(path)-1] {
-		panic("unacceptable path")
+		return nil, ErrUnreachablePath
 	}
 
 	return &TreePos[V]{
 		Node:   node,
 		Offset: path[len(path)-1],
-	}
+	}, nil
 }
 
 // findTextPos returns the tree position of the given path element.
-func findTextPos[V Value](node *Node[V], pathElement int) *TreePos[V] {
+func findTextPos[V Value](node *Node[V], pathElement int) (*TreePos[V], error) {
 	if node.Length < pathElement {
-		panic("unacceptable path")
+		return nil, ErrUnreachablePath
 	}
 
 	for _, childNode := range node.Children() {
@@ -695,31 +746,36 @@ func findTextPos[V Value](node *Node[V], pathElement int) *TreePos[V] {
 	return &TreePos[V]{
 		Node:   node,
 		Offset: pathElement,
-	}
+	}, nil
 }
 
 // FindPostorderRight finds right node of the given tree position with postorder traversal.
-func (t *Tree[V]) FindPostorderRight(pos *TreePos[V]) V {
+func (t *Tree[V]) FindPostorderRight(pos *TreePos[V]) (V, error) {
 	node := pos.Node
 	offset := pos.Offset
 
 	if node.IsText() {
 		if node.Len() == offset {
-			if nextSibling := node.nextSibling(); nextSibling != nil {
-				return nextSibling.Value
+			nextSibling, err := node.nextSibling()
+			if err != nil {
+				return nextSibling.Value, err
+			}
+
+			if nextSibling != nil {
+				return nextSibling.Value, nil
 			}
 
-			return node.Parent.Value
+			return node.Parent.Value, nil
 		}
 
-		return node.Value
+		return node.Value, nil
 	}
 
 	if len(node.Children()) == offset {
-		return node.Value
+		return node.Value, nil
 	}
 
-	return t.FindLeftmost(node.Children()[offset])
+	return t.FindLeftmost(node.Children()[offset]), nil
 }
 
 // GetAncestors returns the ancestors of the given node.
@@ -767,17 +823,21 @@ func (t *Tree[V]) FindLeftmost(node *Node[V]) V {
 }
 
 // IndexOf returns the index of the given node.
-func (t *Tree[V]) IndexOf(node *Node[V]) int {
+func (t *Tree[V]) IndexOf(node *Node[V]) (int, error) {
 	index := 0
 	current := node
 
 	for current != t.root {
 		parent := current.Parent
 		if parent == nil {
-			panic(errors.New("parent is not found"))
+			return 0, errors.New("parent is not found")
+		}
+
+		offset, err := parent.findOffset(current)
+		if err != nil {
+			return 0, err
 		}
 
-		offset := parent.findOffset(current)
 		childrenSlice := parent.Children()[:offset]
 		for _, previous := range childrenSlice {
 			index += previous.PaddedLength()
@@ -792,5 +852,5 @@ func (t *Tree[V]) IndexOf(node *Node[V]) int {
 		current = parent
 	}
 
-	return index
+	return index, nil
 }


Test patch:
diff --git a/pkg/document/crdt/root_test.go b/pkg/document/crdt/root_test.go
index 83eb4d19..64e5c77f 100644
--- a/pkg/document/crdt/root_test.go
+++ b/pkg/document/crdt/root_test.go
@@ -49,7 +49,9 @@ func TestRoot(t *testing.T) {
 		assert.Equal(t, "[0,2]", array.Marshal())
 		assert.Equal(t, 1, root.GarbageLen())
 
-		assert.Equal(t, 1, root.GarbageCollect(time.MaxTicket))
+		n, err := root.GarbageCollect(time.MaxTicket)
+		assert.NoError(t, err)
+		assert.Equal(t, 1, n)
 		assert.Equal(t, 0, root.GarbageLen())
 	})
 
@@ -91,7 +93,9 @@ func TestRoot(t *testing.T) {
 		nodeLen := len(text.Nodes())
 		assert.Equal(t, 4, nodeLen)
 
-		assert.Equal(t, 3, root.GarbageCollect(time.MaxTicket))
+		n, err := root.GarbageCollect(time.MaxTicket)
+		assert.NoError(t, err)
+		assert.Equal(t, 3, n)
 		assert.Equal(t, 0, root.GarbageLen())
 		nodeLen = len(text.Nodes())
 		assert.Equal(t, 1, nodeLen)
@@ -126,7 +130,9 @@ func TestRoot(t *testing.T) {
 			assert.Equal(t, tc.garbage, root.GarbageLen())
 		}
 
-		assert.Equal(t, 3, root.GarbageCollect(time.MaxTicket))
+		n, err := root.GarbageCollect(time.MaxTicket)
+		assert.NoError(t, err)
+		assert.Equal(t, 3, n)
 		assert.Equal(t, 0, root.GarbageLen())
 	})
 
@@ -161,7 +167,9 @@ func TestRoot(t *testing.T) {
 		nodeLen := len(text.Nodes())
 		assert.Equal(t, 3, nodeLen)
 
-		assert.Equal(t, 2, root.GarbageCollect(time.MaxTicket))
+		garbageLen, err := root.GarbageCollect(time.MaxTicket)
+		assert.NoError(t, err)
+		assert.Equal(t, 2, garbageLen)
 		assert.Equal(t, 0, root.GarbageLen())
 
 		nodeLen = len(text.Nodes())
@@ -187,7 +195,9 @@ func TestRoot(t *testing.T) {
 		assert.Equal(t, `{"1":1,"3":3}`, obj.Marshal())
 		assert.Equal(t, 4, root.GarbageLen())
 
-		assert.Equal(t, 4, root.GarbageCollect(time.MaxTicket))
+		n, err := root.GarbageCollect(time.MaxTicket)
+		assert.NoError(t, err)
+		assert.Equal(t, 4, n)
 		assert.Equal(t, 0, root.GarbageLen())
 
 		deleted = obj.Delete("3", ctx.IssueTimeTicket())
@@ -195,7 +205,9 @@ func TestRoot(t *testing.T) {
 		assert.Equal(t, `{"1":1}`, obj.Marshal())
 		assert.Equal(t, 1, root.GarbageLen())
 
-		assert.Equal(t, 1, root.GarbageCollect(time.MaxTicket))
+		n, err = root.GarbageCollect(time.MaxTicket)
+		assert.NoError(t, err)
+		assert.Equal(t, 1, n)
 		assert.Equal(t, 0, root.GarbageLen())
 	})
 }
diff --git a/pkg/document/crdt/tree_test.go b/pkg/document/crdt/tree_test.go
index 8360766d..621671fc 100644
--- a/pkg/document/crdt/tree_test.go
+++ b/pkg/document/crdt/tree_test.go
@@ -39,13 +39,16 @@ func TestTreeNode(t *testing.T) {
 
 	t.Run("element node test", func(t *testing.T) {
 		para := crdt.NewTreeNode(crdt.DummyTreePos, "p", nil)
-		para.Append(crdt.NewTreeNode(crdt.DummyTreePos, "text", nil, "helloyorkie"))
+		err := para.Append(crdt.NewTreeNode(crdt.DummyTreePos, "text", nil, "helloyorkie"))
+		assert.NoError(t, err)
 		assert.Equal(t, "<p>helloyorkie</p>", crdt.ToXML(para))
 		assert.Equal(t, 11, para.Len())
 		assert.Equal(t, false, para.IsText())
 
-		left := para.Child(0)
-		right := left.Split(5)
+		left, err := para.Child(0)
+		assert.NoError(t, err)
+		right, err := left.Split(5)
+		assert.NoError(t, err)
 		assert.Equal(t, "<p>helloyorkie</p>", crdt.ToXML(para))
 		assert.Equal(t, 11, para.Len())
 
@@ -59,7 +62,8 @@ func TestTreeNode(t *testing.T) {
 		attrs := crdt.NewRHT()
 		attrs.Set("font-weight", "bold", time.InitialTicket)
 		node := crdt.NewTreeNode(crdt.DummyTreePos, "span", attrs)
-		node.Append(crdt.NewTreeNode(crdt.DummyTreePos, "text", nil, "helloyorkie"))
+		err := node.Append(crdt.NewTreeNode(crdt.DummyTreePos, "text", nil, "helloyorkie"))
+		assert.NoError(t, err)
 		assert.Equal(t, `<span font-weight="bold">helloyorkie</span>`, crdt.ToXML(node))
 	})
 
@@ -76,11 +80,13 @@ func TestTreeNode(t *testing.T) {
 		}
 		for _, test := range tests {
 			para := crdt.NewTreeNode(crdt.DummyTreePos, "p", nil)
-			para.Append(crdt.NewTreeNode(crdt.DummyTreePos, "text", nil, test.value))
-
-			left := para.Child(0)
+			err := para.Append(crdt.NewTreeNode(crdt.DummyTreePos, "text", nil, test.value))
+			assert.NoError(t, err)
+			left, err := para.Child(0)
+			assert.NoError(t, err)
 			assert.Equal(t, test.length, left.Len())
-			right := left.Split(2)
+			right, err := left.Split(2)
+			assert.NoError(t, err)
 			assert.Equal(t, test.length-2, right.Len())
 		}
 	})
@@ -99,18 +105,20 @@ func TestTree(t *testing.T) {
 
 		//           1
 		// <root> <p> </p> </root>
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<r><p></p></r>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"p", "r"})
 		assert.Equal(t, 2, tree.Root().Len())
 
 		//           1
 		// <root> <p> h e l l o </p> </root>
-		tree.EditByIndex(
+		err = tree.EditByIndex(
 			1, 1,
 			crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "hello"),
 			helper.IssueTime(ctx),
 		)
+		assert.NoError(t, err)
 		assert.Equal(t, "<r><p>hello</p></r>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.hello", "p", "r"})
 		assert.Equal(t, 7, tree.Root().Len())
@@ -118,19 +126,22 @@ func TestTree(t *testing.T) {
 		//       0   1 2 3 4 5 6    7   8 9  10 11 12 13    14
 		// <root> <p> h e l l o </p> <p> w  o  r  l  d  </p>  </root>
 		p := crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil)
-		p.InsertAt(crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "world"), 0)
-		tree.EditByIndex(7, 7, p, helper.IssueTime(ctx))
+		err = p.InsertAt(crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "world"), 0)
+		assert.NoError(t, err)
+		err = tree.EditByIndex(7, 7, p, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<r><p>hello</p><p>world</p></r>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.hello", "p", "text.world", "p", "r"})
 		assert.Equal(t, 14, tree.Root().Len())
 
 		//       0   1 2 3 4 5 6 7    8   9 10 11 12 13 14    15
 		// <root> <p> h e l l o ! </p> <p> w  o  r  l  d  </p>  </root>
-		tree.EditByIndex(
+		err = tree.EditByIndex(
 			6, 6,
 			crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "!"),
 			helper.IssueTime(ctx),
 		)
+		assert.NoError(t, err)
 		assert.Equal(t, "<r><p>hello!</p><p>world</p></r>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.hello", "text.!", "p", "text.world", "p", "r"})
 		assert.Equal(t, crdt.TreeNodeForTest{
@@ -160,11 +171,12 @@ func TestTree(t *testing.T) {
 
 		//       0   1 2 3 4 5 6 7 8    9   10 11 12 13 14 15    16
 		// <root> <p> h e l l o ~ ! </p> <p>  w  o  r  l  d  </p>  </root>
-		tree.EditByIndex(
+		err = tree.EditByIndex(
 			6, 6,
 			crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "~"),
 			helper.IssueTime(ctx),
 		)
+		assert.NoError(t, err)
 		assert.Equal(t, "<r><p>hello~!</p><p>world</p></r>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.hello", "text.~", "text.!", "p", "text.world", "p", "r"})
 	})
@@ -176,10 +188,14 @@ func TestTree(t *testing.T) {
 
 		ctx := helper.TextChangeContext(helper.TestRoot())
 		tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
-		tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ab</p><p>cd</p></root>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.ab", "p", "text.cd", "p", "root"})
 
@@ -191,7 +207,8 @@ func TestTree(t *testing.T) {
 		// 02. Delete b from the first paragraph.
 		// 	     0   1 2    3   4 5 6    7
 		// <root> <p> a </p> <p> c d </p> </root>
-		tree.EditByIndex(2, 3, nil, helper.IssueTime(ctx))
+		err = tree.EditByIndex(2, 3, nil, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>a</p><p>cd</p></root>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.a", "p", "text.cd", "p", "root"})
 
@@ -208,17 +225,22 @@ func TestTree(t *testing.T) {
 
 		ctx := helper.TextChangeContext(helper.TestRoot())
 		tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
-		tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ab</p><p>cd</p></root>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.ab", "p", "text.cd", "p", "root"})
 
 		// 02. delete b, c and first paragraph.
 		//       0   1 2 3    4
 		// <root> <p> a d </p> </root>
-		tree.EditByIndex(2, 6, nil, helper.IssueTime(ctx))
+		err = tree.EditByIndex(2, 6, nil, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ad</p></root>", tree.ToXML())
 		helper.ListEqual(t, tree, []string{"text.a", "text.d", "p", "root"})
 
@@ -229,7 +251,8 @@ func TestTree(t *testing.T) {
 		assert.Equal(t, 1, structure.Children[0].Children[1].Size)
 
 		// 03. insert a new text node at the start of the first paragraph.
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "@"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "@"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>@ad</p></root>", tree.ToXML())
 	})
 
@@ -239,75 +262,113 @@ func TestTree(t *testing.T) {
 		// <root> <p> <b> <i> a b </i> </b> </p> </root>
 		ctx := helper.TextChangeContext(helper.TestRoot())
 		tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
-		tree.EditByIndex(5, 6, nil, helper.IssueTime(ctx))
+		err = tree.EditByIndex(5, 6, nil, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b>ab</b></p></root>", tree.ToXML())
 
 		// 02. Edit between two element nodes in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
-		tree.EditByIndex(6, 7, nil, helper.IssueTime(ctx))
+		err = tree.EditByIndex(6, 7, nil, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><i>ab</i></p></root>", tree.ToXML())
 
 		// 03. Edit between text and element node in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
-		tree.EditByIndex(4, 6, nil, helper.IssueTime(ctx))
+		err = tree.EditByIndex(4, 6, nil, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b>a</b></p></root>", tree.ToXML())
 
 		// 04. Edit between text and element node in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
-		tree.EditByIndex(5, 7, nil, helper.IssueTime(ctx))
+		err = tree.EditByIndex(5, 7, nil, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ab</p></root>", tree.ToXML())
 
 		// 05. Edit between text and element node in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
-		tree.EditByIndex(4, 7, nil, helper.IssueTime(ctx))
+		err = tree.EditByIndex(4, 7, nil, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>a</p></root>", tree.ToXML())
 
 		// 06. Edit between text and element node in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(2, 2, crdt.NewTreeNode(helper.IssuePos(ctx), "i", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(3, 3, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p><b><i>ab</i></b></p></root>", tree.ToXML())
-		tree.EditByIndex(3, 7, nil, helper.IssueTime(ctx))
+		err = tree.EditByIndex(3, 7, nil, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p></p></root>", tree.ToXML())
 
 		// 07. Edit between text and element node in same hierarchy.
 		tree = crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
-		tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(6, 6, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
-		tree.EditByIndex(10, 10, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(11, 11, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ef"), helper.IssueTime(ctx))
+		err = tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "b", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(6, 6, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(10, 10, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(11, 11, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ef"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ab</p><p><b>cd</b></p><p>ef</p></root>", tree.ToXML())
-		tree.EditByIndex(9, 10, nil, helper.IssueTime(ctx))
+		err = tree.EditByIndex(9, 10, nil, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ab</p><b>cd</b><p>ef</p></root>", tree.ToXML())
 	})
 
@@ -315,21 +376,28 @@ func TestTree(t *testing.T) {
 		// 01. style attributes to an element node.
 		ctx := helper.TextChangeContext(helper.TestRoot())
 		tree := crdt.NewTree(crdt.NewTreeNode(helper.IssuePos(ctx), "root", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
-		tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
-		tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		err := tree.EditByIndex(0, 0, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(1, 1, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "ab"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(4, 4, crdt.NewTreeNode(helper.IssuePos(ctx), "p", nil), helper.IssueTime(ctx))
+		assert.NoError(t, err)
+		err = tree.EditByIndex(5, 5, crdt.NewTreeNode(helper.IssuePos(ctx), "text", nil, "cd"), helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, "<root><p>ab</p><p>cd</p></root>", tree.ToXML())
 
-		tree.StyleByIndex(3, 4, map[string]string{"weight": "bold"}, helper.IssueTime(ctx))
+		err = tree.StyleByIndex(3, 4, map[string]string{"weight": "bold"}, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, `<root><p weight="bold">ab</p><p>cd</p></root>`, tree.ToXML())
 
 		// 02. style attributes to elements.
-		tree.StyleByIndex(3, 8, map[string]string{"style": "italic"}, helper.IssueTime(ctx))
+		err = tree.StyleByIndex(3, 8, map[string]string{"style": "italic"}, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, `<root><p style="italic" weight="bold">ab</p><p style="italic">cd</p></root>`, tree.ToXML())
 
 		// 03. style attributes to text nodes.
-		tree.StyleByIndex(1, 3, map[string]string{"style": "italic"}, helper.IssueTime(ctx))
+		err = tree.StyleByIndex(1, 3, map[string]string{"style": "italic"}, helper.IssueTime(ctx))
+		assert.NoError(t, err)
 		assert.Equal(t, `<root><p style="italic" weight="bold">ab</p><p style="italic">cd</p></root>`, tree.ToXML())
 	})
 }
diff --git a/pkg/index/tree_test.go b/pkg/index/tree_test.go
index 126fb5b7..d2d87614 100644
--- a/pkg/index/tree_test.go
+++ b/pkg/index/tree_test.go
@@ -38,30 +38,38 @@ func TestIndexTree(t *testing.T) {
 			},
 		})
 
-		pos := tree.FindTreePos(0)
+		pos, err := tree.FindTreePos(0)
 		assert.Equal(t, "r", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
-		pos = tree.FindTreePos(1)
+		assert.NoError(t, err)
+		pos, err = tree.FindTreePos(1)
 		assert.Equal(t, "text.hello", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
-		pos = tree.FindTreePos(6)
+		assert.NoError(t, err)
+		pos, err = tree.FindTreePos(6)
 		assert.Equal(t, "text.hello", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 5, pos.Offset)
-		pos = tree.FindTreePos(6, false)
+		assert.NoError(t, err)
+		pos, err = tree.FindTreePos(6, false)
 		assert.Equal(t, "p", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 1, pos.Offset)
-		pos = tree.FindTreePos(7)
+		assert.NoError(t, err)
+		pos, err = tree.FindTreePos(7)
 		assert.Equal(t, "r", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 1, pos.Offset)
-		pos = tree.FindTreePos(8)
+		assert.NoError(t, err)
+		pos, err = tree.FindTreePos(8)
 		assert.Equal(t, "text.world", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
-		pos = tree.FindTreePos(13)
+		assert.NoError(t, err)
+		pos, err = tree.FindTreePos(13)
 		assert.Equal(t, "text.world", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 5, pos.Offset)
-		pos = tree.FindTreePos(14)
+		assert.NoError(t, err)
+		pos, err = tree.FindTreePos(14)
 		assert.Equal(t, "r", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 2, pos.Offset)
+		assert.NoError(t, err)
 	})
 
 	t.Run("find right node from the given offset in postorder traversal test", func(t *testing.T) {
@@ -76,13 +84,48 @@ func TestIndexTree(t *testing.T) {
 		})
 
 		// postorder traversal: "ab", <b>, "cd", <p>, <root>
-		assert.Equal(t, "text", tree.FindPostorderRight(tree.FindTreePos(0)).Type())
-		assert.Equal(t, "text", tree.FindPostorderRight(tree.FindTreePos(1)).Type())
-		assert.Equal(t, "p", tree.FindPostorderRight(tree.FindTreePos(3)).Type())
-		assert.Equal(t, "text", tree.FindPostorderRight(tree.FindTreePos(4)).Type())
-		assert.Equal(t, "text", tree.FindPostorderRight(tree.FindTreePos(5)).Type())
-		assert.Equal(t, "p", tree.FindPostorderRight(tree.FindTreePos(7)).Type())
-		assert.Equal(t, "root", tree.FindPostorderRight(tree.FindTreePos(8)).Type())
+
+		treePos, posErr := tree.FindTreePos(0)
+		assert.NoError(t, posErr)
+		right, rightErr := tree.FindPostorderRight(treePos)
+		assert.NoError(t, rightErr)
+		assert.Equal(t, "text", right.Type())
+
+		treePos, posErr = tree.FindTreePos(1)
+		assert.NoError(t, posErr)
+		right, rightErr = tree.FindPostorderRight(treePos)
+		assert.NoError(t, rightErr)
+		assert.Equal(t, "text", right.Type())
+
+		treePos, posErr = tree.FindTreePos(3)
+		assert.NoError(t, posErr)
+		right, rightErr = tree.FindPostorderRight(treePos)
+		assert.NoError(t, rightErr)
+		assert.Equal(t, "p", right.Type())
+
+		treePos, posErr = tree.FindTreePos(4)
+		assert.NoError(t, posErr)
+		right, rightErr = tree.FindPostorderRight(treePos)
+		assert.NoError(t, rightErr)
+		assert.Equal(t, "text", right.Type())
+
+		treePos, posErr = tree.FindTreePos(5)
+		assert.NoError(t, posErr)
+		right, rightErr = tree.FindPostorderRight(treePos)
+		assert.NoError(t, rightErr)
+		assert.Equal(t, "text", right.Type())
+
+		treePos, posErr = tree.FindTreePos(7)
+		assert.NoError(t, posErr)
+		right, rightErr = tree.FindPostorderRight(treePos)
+		assert.NoError(t, rightErr)
+		assert.Equal(t, "p", right.Type())
+
+		treePos, posErr = tree.FindTreePos(8)
+		assert.NoError(t, posErr)
+		right, rightErr = tree.FindPostorderRight(treePos)
+		assert.NoError(t, rightErr)
+		assert.Equal(t, "root", right.Type())
 	})
 
 	t.Run("find common ancestor of two given nodes test", func(t *testing.T) {
@@ -97,8 +140,11 @@ func TestIndexTree(t *testing.T) {
 			},
 		})
 
-		nodeAB := tree.FindTreePos(3, true).Node
-		nodeCD := tree.FindTreePos(7, true).Node
+		treePosAB, _ := tree.FindTreePos(3, true)
+		nodeAB := treePosAB.Node
+
+		treePosCD, _ := tree.FindTreePos(7, true)
+		nodeCD := treePosCD.Node
 
 		assert.Equal(t, "text.ab", helper.ToDiagnostic(nodeAB.Value))
 		assert.Equal(t, "text.cd", helper.ToDiagnostic(nodeCD.Value))
@@ -144,42 +190,63 @@ func TestIndexTree(t *testing.T) {
 			},
 		})
 
-		pos := tree.FindTreePos(0, true)
+		pos, posErr := tree.FindTreePos(0, true)
+		assert.NoError(t, posErr)
 		assert.Equal(t, "root", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
-		assert.Equal(t, 0, tree.IndexOf(pos.Node))
+		index, indexErr := tree.IndexOf(pos.Node)
+		assert.NoError(t, indexErr)
+		assert.Equal(t, 0, index)
 
-		pos = tree.FindTreePos(1, true)
+		pos, posErr = tree.FindTreePos(1, true)
+		assert.NoError(t, posErr)
 		assert.Equal(t, "text.a", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
-		assert.Equal(t, 1, tree.IndexOf(pos.Node))
+		index, indexErr = tree.IndexOf(pos.Node)
+		assert.NoError(t, indexErr)
+		assert.Equal(t, 1, index)
 
-		pos = tree.FindTreePos(3, true)
+		pos, posErr = tree.FindTreePos(3, true)
+		assert.NoError(t, posErr)
 		assert.Equal(t, "text.b", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 1, pos.Offset)
-		assert.Equal(t, 2, tree.IndexOf(pos.Node))
+		index, indexErr = tree.IndexOf(pos.Node)
+		assert.NoError(t, indexErr)
+		assert.Equal(t, 2, index)
 
-		pos = tree.FindTreePos(4, true)
+		pos, posErr = tree.FindTreePos(4, true)
+		assert.NoError(t, posErr)
 		assert.Equal(t, "root", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 1, pos.Offset)
-		assert.Equal(t, 0, tree.IndexOf(pos.Node))
+		index, indexErr = tree.IndexOf(pos.Node)
+		assert.NoError(t, indexErr)
+		assert.Equal(t, 0, index)
 
-		pos = tree.FindTreePos(10, true)
+		pos, posErr = tree.FindTreePos(10, true)
+		assert.NoError(t, posErr)
 		assert.Equal(t, "text.fg", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
-		assert.Equal(t, 10, tree.IndexOf(pos.Node))
+		index, indexErr = tree.IndexOf(pos.Node)
+		assert.NoError(t, indexErr)
+		assert.Equal(t, 10, index)
 
 		firstP := tree.Root().Children()[0]
+		index, indexErr = tree.IndexOf(firstP)
+		assert.NoError(t, indexErr)
 		assert.Equal(t, "p", helper.ToDiagnostic(firstP.Value))
-		assert.Equal(t, 0, tree.IndexOf(firstP))
+		assert.Equal(t, 0, index)
 
 		secondP := tree.Root().Children()[1]
+		index, indexErr = tree.IndexOf(secondP)
+		assert.NoError(t, indexErr)
 		assert.Equal(t, "p", helper.ToDiagnostic(secondP.Value))
-		assert.Equal(t, 4, tree.IndexOf(secondP))
+		assert.Equal(t, 4, index)
 
 		thirdP := tree.Root().Children()[2]
+		index, indexErr = tree.IndexOf(thirdP)
+		assert.NoError(t, indexErr)
 		assert.Equal(t, "p", helper.ToDiagnostic(thirdP.Value))
-		assert.Equal(t, 9, tree.IndexOf(thirdP))
+		assert.Equal(t, 9, index)
 	})
 
 	t.Run("find treePos from given path test", func(t *testing.T) {
@@ -200,71 +267,88 @@ func TestIndexTree(t *testing.T) {
 			},
 		})
 
-		pos := tree.PathToTreePos([]int{0})
+		pos, err := tree.PathToTreePos([]int{0})
+		assert.NoError(t, err)
 		assert.Equal(t, "root", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{0, 0})
+		pos, err = tree.PathToTreePos([]int{0, 0})
+		assert.NoError(t, err)
 		assert.Equal(t, "p", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{0, 0, 0})
+		pos, err = tree.PathToTreePos([]int{0, 0, 0})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.a", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{0, 0, 1})
+		pos, err = tree.PathToTreePos([]int{0, 0, 1})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.a", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 1, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{0, 1, 0})
+		pos, err = tree.PathToTreePos([]int{0, 1, 0})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.b", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{0, 1, 1})
+		pos, err = tree.PathToTreePos([]int{0, 1, 1})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.b", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 1, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{1})
+		pos, err = tree.PathToTreePos([]int{1})
+		assert.NoError(t, err)
 		assert.Equal(t, "p", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{1, 0})
+		pos, err = tree.PathToTreePos([]int{1, 0})
+		assert.NoError(t, err)
 		assert.Equal(t, "p", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{1, 0, 0})
+		pos, err = tree.PathToTreePos([]int{1, 0, 0})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.cde", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{1, 0, 1})
+		pos, err = tree.PathToTreePos([]int{1, 0, 1})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.cde", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 1, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{1, 0, 2})
+		pos, err = tree.PathToTreePos([]int{1, 0, 2})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.cde", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 2, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{1, 0, 3})
+		pos, err = tree.PathToTreePos([]int{1, 0, 3})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.cde", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 3, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{2})
+		pos, err = tree.PathToTreePos([]int{2})
+		assert.NoError(t, err)
 		assert.Equal(t, "p", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 1, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{2, 0, 0})
+		pos, err = tree.PathToTreePos([]int{2, 0, 0})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.fg", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 0, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{2, 0, 1})
+		pos, err = tree.PathToTreePos([]int{2, 0, 1})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.fg", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 1, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{2, 0, 2})
+		pos, err = tree.PathToTreePos([]int{2, 0, 2})
+		assert.NoError(t, err)
 		assert.Equal(t, "text.fg", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 2, pos.Offset)
 
-		pos = tree.PathToTreePos([]int{3})
+		pos, err = tree.PathToTreePos([]int{3})
+		assert.NoError(t, err)
 		assert.Equal(t, "p", helper.ToDiagnostic(pos.Node.Value))
 		assert.Equal(t, 2, pos.Offset)
 	})
@@ -289,55 +373,106 @@ func TestIndexTree(t *testing.T) {
 
 		//       0  1  2    3 4 5 6 7     8   9 10 11 12 13  14 15  16
 		// <root><tc><p><tn> A B C D </tn><tn> E  F G  H </tn><p></tc></root>
-		pos := tree.FindTreePos(0)
-		assert.Equal(t, []int{0}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(1)
-		assert.Equal(t, []int{0, 0}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(2)
-		assert.Equal(t, []int{0, 0, 0}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(3)
-		assert.Equal(t, []int{0, 0, 0, 0}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(4)
-		assert.Equal(t, []int{0, 0, 0, 1}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(5)
-		assert.Equal(t, []int{0, 0, 0, 2}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(6)
-		assert.Equal(t, []int{0, 0, 0, 3}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(7)
-		assert.Equal(t, []int{0, 0, 0, 4}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(8)
-		assert.Equal(t, []int{0, 0, 1}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(9)
-		assert.Equal(t, []int{0, 0, 1, 0}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(10)
-		assert.Equal(t, []int{0, 0, 1, 1}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(11)
-		assert.Equal(t, []int{0, 0, 1, 2}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(12)
-		assert.Equal(t, []int{0, 0, 1, 3}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(13)
-		assert.Equal(t, []int{0, 0, 1, 4}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(14)
-		assert.Equal(t, []int{0, 0, 2}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(15)
-		assert.Equal(t, []int{0, 1}, tree.TreePosToPath(pos))
-
-		pos = tree.FindTreePos(16)
-		assert.Equal(t, []int{1}, tree.TreePosToPath(pos))
+		pos, posErr := tree.FindTreePos(0)
+		assert.NoError(t, posErr)
+		path, pathErr := tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0}, path)
+
+		pos, posErr = tree.FindTreePos(1)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0}, path)
+
+		pos, posErr = tree.FindTreePos(2)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 0}, path)
+
+		pos, posErr = tree.FindTreePos(3)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 0, 0}, path)
+
+		pos, posErr = tree.FindTreePos(4)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 0, 1}, path)
+
+		pos, posErr = tree.FindTreePos(5)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 0, 2}, path)
+
+		pos, posErr = tree.FindTreePos(6)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 0, 3}, path)
+
+		pos, posErr = tree.FindTreePos(7)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 0, 4}, path)
+
+		pos, posErr = tree.FindTreePos(8)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 1}, path)
+
+		pos, posErr = tree.FindTreePos(9)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 1, 0}, path)
+
+		pos, posErr = tree.FindTreePos(10)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 1, 1}, path)
+
+		pos, posErr = tree.FindTreePos(11)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 1, 2}, path)
+
+		pos, posErr = tree.FindTreePos(12)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 1, 3}, path)
+
+		pos, posErr = tree.FindTreePos(13)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 1, 4}, path)
+
+		pos, posErr = tree.FindTreePos(14)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 0, 2}, path)
+
+		pos, posErr = tree.FindTreePos(15)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{0, 1}, path)
+
+		pos, posErr = tree.FindTreePos(16)
+		assert.NoError(t, posErr)
+		path, pathErr = tree.TreePosToPath(pos)
+		assert.NoError(t, pathErr)
+		assert.Equal(t, []int{1}, path)
 	})
 }
diff --git a/test/helper/helper.go b/test/helper/helper.go
index a2a744d9..b324d5f2 100644
--- a/test/helper/helper.go
+++ b/test/helper/helper.go
@@ -150,9 +150,10 @@ func ListEqual(t assert.TestingT, tree *crdt.Tree, expected []string) bool {
 // indexes.
 func NodesBetweenEqual(t assert.TestingT, tree *index.Tree[*crdt.TreeNode], from, to int, expected []string) bool {
 	var nodes []*crdt.TreeNode
-	tree.NodesBetween(from, to, func(node *crdt.TreeNode) {
+	err := tree.NodesBetween(from, to, func(node *crdt.TreeNode) {
 		nodes = append(nodes, node)
 	})
+	assert.NoError(t, err)
 
 	var actual []string
 	for _, node := range nodes {