diff --git a/contracts/base/ModuleManager.sol b/contracts/base/ModuleManager.sol
index 3db31b11c..ac3fe35cd 100644
--- a/contracts/base/ModuleManager.sol
+++ b/contracts/base/ModuleManager.sol
@@ -107,23 +107,37 @@ contract ModuleManager is SelfAuthorized, Executor {
     }
 
     /// @dev Returns array of modules.
-    /// @param start Start of the page.
-    /// @param pageSize Maximum number of modules that should be returned.
+    ///      If all entries fit into a single page, the next pointer will be 0x1.
+    ///      If another page is present, next will be the last element of the returned array.
+    /// @param start Start of the page. Has to be a module or start pointer (0x1 address)
+    /// @param pageSize Maximum number of modules that should be returned. Has to be > 0
     /// @return array Array of modules.
     /// @return next Start of the next page.
     function getModulesPaginated(address start, uint256 pageSize) external view returns (address[] memory array, address next) {
+        require(start == SENTINEL_MODULES || isModuleEnabled(start), "GS105");
+        require(pageSize > 0, "GS106");
         // Init array with max page size
         array = new address[](pageSize);
 
         // Populate return array
         uint256 moduleCount = 0;
-        address currentModule = modules[start];
-        while (currentModule != address(0x0) && currentModule != SENTINEL_MODULES && moduleCount < pageSize) {
-            array[moduleCount] = currentModule;
-            currentModule = modules[currentModule];
+        next = modules[start];
+        while (next != address(0) && next != SENTINEL_MODULES && moduleCount < pageSize) {
+            array[moduleCount] = next;
+            next = modules[next];
             moduleCount++;
         }
-        next = currentModule;
+
+        // Because of the argument validation we can assume that
+        // the `currentModule` will always be either a module address
+        // or sentinel address (aka the end). If we haven't reached the end
+        // inside the loop, we need to set the next pointer to the last element
+        // because it skipped over to the next module which is neither included
+        // in the current page nor won't be included in the next one
+        // if you pass it as a start.
+        if (next != SENTINEL_MODULES) {
+            next = array[moduleCount - 1];
+        }
         // Set correct size of returned array
         // solhint-disable-next-line no-inline-assembly
         assembly {
diff --git a/docs/error_codes.md b/docs/error_codes.md
index c856d1070..4611d18db 100644
--- a/docs/error_codes.md
+++ b/docs/error_codes.md
@@ -29,6 +29,8 @@
 - `GS102`: `Module has already been added`
 - `GS103`: `Invalid prevModule, module pair provided`
 - `GS104`: `Method can only be called from an enabled module`
+- `GS105`: `Invalid starting point for fetching paginated modules`
+- `GS106`: `Invalid page size for fetching paginated modules`
 
 ### Owner management related
 - `GS200`: `Owners have already been set up`
diff --git a/test/core/GnosisSafe.ModuleManager.spec.ts b/test/core/GnosisSafe.ModuleManager.spec.ts
index 1cf700a48..e480d0ddd 100644
--- a/test/core/GnosisSafe.ModuleManager.spec.ts
+++ b/test/core/GnosisSafe.ModuleManager.spec.ts
@@ -9,7 +9,7 @@ import { AddressOne } from "../../src/utils/constants";
 
 describe("ModuleManager", async () => {
 
-    const [user1, user2] = waffle.provider.getWallets();
+    const [user1, user2, user3] = waffle.provider.getWallets();
 
     const setupTests = deployments.createFixture(async ({ deployments }) => {
         await deployments.fixture();
@@ -50,7 +50,7 @@ describe("ModuleManager", async () => {
             ).to.revertedWith("GS013")
         })
 
-        it('emits event for new module', async () => {
+        it('emits event for a new module', async () => {
             const { safe } = await setupTests()
             await expect(
                 executeContractCallWithSigners(safe, safe, "enableModule", [user2.address], [user1])
@@ -244,4 +244,52 @@ describe("ModuleManager", async () => {
             ).to.be.deep.eq([false, "0x08c379a000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000013536f6d652072616e646f6d206d65737361676500000000000000000000000000"])
         })
     })
+
+    describe("getModulesPaginated", async () => {
+        it('requires page size to be greater than 0', async () => {
+            const { safe } = await setupTests()
+            await expect(safe.getModulesPaginated(AddressOne, 0)).to.be.revertedWith("GS106")
+        })
+
+        it('requires start to be a module or start pointer', async () => {
+            const { safe } = await setupTests()
+            
+            await expect(safe.getModulesPaginated(AddressZero, 1)).to.be.reverted
+            await executeContractCallWithSigners(safe, safe, "enableModule", [user1.address], [user1])
+            expect(await safe.getModulesPaginated(user1.address, 1)).to.be.deep.equal([[], AddressOne])
+            await expect(safe.getModulesPaginated(user2.address, 1)).to.be.revertedWith("GS105")
+        })
+
+        it('Returns all modules over multiple pages', async () => {
+            const { safe } = await setupTests()
+            await expect(
+                executeContractCallWithSigners(safe, safe, "enableModule", [user1.address], [user1])
+            ).to.emit(safe, "EnabledModule").withArgs(user1.address)
+
+            await expect(
+                executeContractCallWithSigners(safe, safe, "enableModule", [user2.address], [user1])
+            ).to.emit(safe, "EnabledModule").withArgs(user2.address)
+
+            await expect(
+                executeContractCallWithSigners(safe, safe, "enableModule", [user3.address], [user1])
+            ).to.emit(safe, "EnabledModule").withArgs(user3.address)
+
+            await expect(await safe.isModuleEnabled(user1.address)).to.be.true
+            await expect(await safe.isModuleEnabled(user2.address)).to.be.true
+            await expect(await safe.isModuleEnabled(user3.address)).to.be.true
+            /*
+            This will pass the test which is not correct
+            await expect(await safe.getModulesPaginated(AddressOne, 1)).to.be.deep.equal([[user3.address], user2.address])
+            await expect(await safe.getModulesPaginated(user2.address, 1)).to.be.deep.equal([[user1.address], AddressOne])
+            */
+            await expect(await safe.getModulesPaginated(AddressOne, 1)).to.be.deep.equal([[user3.address], user3.address])
+            await expect(await safe.getModulesPaginated(user3.address, 1)).to.be.deep.equal([[user2.address], user2.address])
+            await expect(await safe.getModulesPaginated(user2.address, 1)).to.be.deep.equal([[user1.address], AddressOne])
+        })
+
+        it('returns an empty array and end pointer for a safe with no modules', async () => {
+            const { safe } = await setupTests()
+            expect(await safe.getModulesPaginated(AddressOne, 10)).to.be.deep.equal([[], AddressOne])
+        })
+    })
 })
\ No newline at end of file
diff --git a/test/core/GnosisSafe.Setup.spec.ts b/test/core/GnosisSafe.Setup.spec.ts
index dc3de7d1d..be62bc7f7 100644
--- a/test/core/GnosisSafe.Setup.spec.ts
+++ b/test/core/GnosisSafe.Setup.spec.ts
@@ -29,10 +29,15 @@ describe("GnosisSafe", async () => {
             await expect(
                 await singleton.getThreshold()
             ).to.be.deep.eq(BigNumber.from(1))
-            await expect(
-                await singleton.getModulesPaginated(AddressOne, 10)
-            ).to.be.deep.eq([[], AddressZero])
 
+            // Because setup wasn't called on the singleton it breaks the assumption made 
+            // within `getModulesPaginated` method that the linked list will be always correctly
+            // initialized with 0x1 as a starting element and 0x1 as the end
+            // But because `setupModules` wasn't called, it is empty.
+            await expect(
+                singleton.getModulesPaginated(AddressOne, 10)
+            ).to.be.reverted
+    
             // "Should not be able to retrieve owners (currently the contract will run in an endless loop when not initialized)"
             await expect(
                 singleton.getOwners()