diff --git a/cmd/soroban-rpc/internal/db/cursor.go b/cmd/soroban-rpc/internal/db/cursor.go
index 4102038d..8f3be397 100644
--- a/cmd/soroban-rpc/internal/db/cursor.go
+++ b/cmd/soroban-rpc/internal/db/cursor.go
@@ -7,7 +7,9 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/stellar/go/support/ordered"
 	"github.com/stellar/go/toid"
+	"golang.org/x/exp/constraints"
 )
 
 const (
@@ -40,9 +42,15 @@ type CursorRange struct {
 
 // String returns a string representation of this cursor
 func (c Cursor) String() string {
+	// TOID maxes out at int32, so we need to clamp accordingly to avoid
+	// generating invalid cursors
+	ledger := int32(clamp(c.Ledger, 0, math.MaxInt32))
+	tx := int32(clamp(c.Tx, 0, math.MaxInt32))
+	op := int32(clamp(c.Op, 0, math.MaxInt32))
+
 	return fmt.Sprintf(
 		"%019d-%010d",
-		toid.New(int32(c.Ledger), int32(c.Tx), int32(c.Op)).ToInt64(),
+		toid.New(ledger, tx, op).ToInt64(),
 		c.Event,
 	)
 }
@@ -95,16 +103,6 @@ func ParseCursor(input string) (Cursor, error) {
 	}, nil
 }
 
-func cmp(a, b uint32) int {
-	if a < b {
-		return -1
-	}
-	if a > b {
-		return 1
-	}
-	return 0
-}
-
 // Cmp compares two cursors.
 // 0 is returned if the c is equal to other.
 // 1 is returned if c is greater than other.
@@ -122,6 +120,20 @@ func (c Cursor) Cmp(other Cursor) int {
 	return cmp(c.Ledger, other.Ledger)
 }
 
+func cmp(a, b uint32) int {
+	if a < b {
+		return -1
+	}
+	if a > b {
+		return 1
+	}
+	return 0
+}
+
+func clamp[T constraints.Ordered](val, floor, ceil T) T {
+	return ordered.Min(ceil, ordered.Max(floor, val))
+}
+
 var (
 	// MinCursor is the smallest possible cursor
 	//nolint:gochecknoglobals