diff --git a/dist/cjs/index.cjs b/dist/cjs/index.cjs
index 52ae6ca12f8d1c650ee7f1bd55573ee7d4f8b65f..830f646f5bd80f4e00f442291ad6e4c6c508630d 100644
--- a/dist/cjs/index.cjs
+++ b/dist/cjs/index.cjs
@@ -716,17 +716,20 @@ class Virtualizer {
         this.getMeasurements(),
         this.getSize(),
         this.getScrollOffset(),
-        this.options.lanes
+        this.options.lanes,
+        this.options.paddingEnd
       ],
-      (measurements, outerSize, scrollOffset, lanes) => {
+      (measurements, outerSize, scrollOffset, lanes, _paddingEnd) => {
         if (measurements.length === 0 || outerSize === 0) {
           this.range = null;
           return null;
         }
+        const maxScrollOffset = Math.max(this.options.scrollMargin + this.getTotalSize() - outerSize, 0);
+        const effectiveScrollOffset = Math.min(Math.max(scrollOffset, 0), maxScrollOffset);
         this.range = calculateRangeImpl(
           measurements,
           outerSize,
-          scrollOffset,
+          effectiveScrollOffset,
           lanes,
           // Pass the typed array so binary search + forward-walk can read
           // start/end directly from Float64Array, skipping the Proxy traps.
diff --git a/dist/cjs/index.d.cts b/dist/cjs/index.d.cts
index c61ee17752565253f795c7fc7d57e86237ecbb52..430c622a18c69407df9f1ca82ee9440e9be287b7 100644
--- a/dist/cjs/index.d.cts
+++ b/dist/cjs/index.d.cts
@@ -144,7 +144,7 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
             startIndex: number;
             endIndex: number;
         } | null;
-        updateDeps(newDeps: [VirtualItem[], number, number, number]): void;
+        updateDeps(newDeps: [VirtualItem[], number, number, number, number]): void;
     };
     getVirtualIndexes: {
         (): number[];
diff --git a/dist/esm/index.d.ts b/dist/esm/index.d.ts
index b03abab604eb6578f6f56ff92c489259cfaf8f19..940310f9215ba5bf666c5b71e92dddbbbc85635d 100644
--- a/dist/esm/index.d.ts
+++ b/dist/esm/index.d.ts
@@ -144,7 +144,7 @@ export declare class Virtualizer<TScrollElement extends Element | Window, TItemE
             startIndex: number;
             endIndex: number;
         } | null;
-        updateDeps(newDeps: [VirtualItem[], number, number, number]): void;
+        updateDeps(newDeps: [VirtualItem[], number, number, number, number]): void;
     };
     getVirtualIndexes: {
         (): number[];
diff --git a/dist/esm/index.js b/dist/esm/index.js
index 3032c0ca457582be3f47923cba1f7d92c848745c..6e52124048fb3bc7b7ee37e763354b33593a6285 100644
--- a/dist/esm/index.js
+++ b/dist/esm/index.js
@@ -714,17 +714,20 @@ class Virtualizer {
         this.getMeasurements(),
         this.getSize(),
         this.getScrollOffset(),
-        this.options.lanes
+        this.options.lanes,
+        this.options.paddingEnd
       ],
-      (measurements, outerSize, scrollOffset, lanes) => {
+      (measurements, outerSize, scrollOffset, lanes, _paddingEnd) => {
         if (measurements.length === 0 || outerSize === 0) {
           this.range = null;
           return null;
         }
+        const maxScrollOffset = Math.max(this.options.scrollMargin + this.getTotalSize() - outerSize, 0);
+        const effectiveScrollOffset = Math.min(Math.max(scrollOffset, 0), maxScrollOffset);
         this.range = calculateRangeImpl(
           measurements,
           outerSize,
-          scrollOffset,
+          effectiveScrollOffset,
           lanes,
           // Pass the typed array so binary search + forward-walk can read
           // start/end directly from Float64Array, skipping the Proxy traps.
diff --git a/src/index.ts b/src/index.ts
index 7ad79aacd591c3a4f7855c91b2727a59a9579bca..201406779ee85d2f2e5b01a8ea284aa9a5f694e2 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1367,16 +1367,19 @@ export class Virtualizer<
       this.getSize(),
       this.getScrollOffset(),
       this.options.lanes,
+      this.options.paddingEnd,
     ],
-    (measurements, outerSize, scrollOffset, lanes) => {
+    (measurements, outerSize, scrollOffset, lanes, _paddingEnd) => {
       if (measurements.length === 0 || outerSize === 0) {
         this.range = null
         return null
       }
+      const maxScrollOffset = Math.max(this.options.scrollMargin + this.getTotalSize() - outerSize, 0)
+      const effectiveScrollOffset = Math.min(Math.max(scrollOffset, 0), maxScrollOffset)
       this.range = calculateRangeImpl(
         measurements,
         outerSize,
-        scrollOffset,
+        effectiveScrollOffset,
         lanes,
         // Pass the typed array so binary search + forward-walk can read
         // start/end directly from Float64Array, skipping the Proxy traps.
