diff --git a/src/lua/socket.lua b/src/lua/socket.lua
index a334ad45ba31e1d4d92f02aa1763194430fd71e0..bbdef01e36c3dbe5dc1cb410704d280bfd04d9a3 100644
--- a/src/lua/socket.lua
+++ b/src/lua/socket.lua
@@ -73,12 +73,17 @@ local gc_socket_t = ffi.metatype(ffi.typeof('struct gc_socket'), {
 
 local socket_mt
 
+local function socket_is_closed(socket)
+    -- The type of 'socket' is not checked because the function
+    -- is internal and the type must be checked outside if needed.
+    return socket._gc_socket.fd < 0
+end
+
 local function check_socket(socket)
     local gc_socket = type(socket) == 'table' and socket._gc_socket
     if ffi.istype(gc_socket_t, gc_socket) then
-        local fd = gc_socket.fd
-        if fd >= 0 then
-            return fd
+        if not socket_is_closed(socket) then
+            return gc_socket.fd
         else
             error("attempt to use closed socket")
         end
@@ -1082,6 +1087,9 @@ local function tcp_server_loop(server, s, addr)
     fiber.name(format("%s/%s:%s", server.name, addr.host, addr.port), {truncate = true})
     log.info("started")
     while socket_readable(s) do
+        if socket_is_closed(s) then
+            break
+        end
         local sc, from = socket_accept(s)
         if sc == nil then
             local errno = s._errno
diff --git a/test/app/socket.result b/test/app/socket.result
index 9f0ea0a5da8d7b4b2d40e11fdb3bb04147a5b39e..6206848f2da87b0d8c2b67bb28d356be5c47da1f 100644
--- a/test/app/socket.result
+++ b/test/app/socket.result
@@ -2914,3 +2914,17 @@ srv:close()
 ---
 - true
 ...
+--
+-- gh-4087: fix error while closing socket.tcp_server
+--
+srv = socket.tcp_server('127.0.0.1', 0, function() end)
+---
+...
+srv:close()
+---
+- true
+...
+test_run:wait_log('default', 'stopped', 1024, 0.01)
+---
+- stopped
+...
diff --git a/test/app/socket.test.lua b/test/app/socket.test.lua
index d1fe7ada686db0b825eaec95ca9a9700cadad57d..a399fca3eda5c4d37725d0b5bd67caf2a5657693 100644
--- a/test/app/socket.test.lua
+++ b/test/app/socket.test.lua
@@ -987,4 +987,9 @@ s:settimeout(1)
 s:receive('*a')
 s:close()
 srv:close()
-
+--
+-- gh-4087: fix error while closing socket.tcp_server
+--
+srv = socket.tcp_server('127.0.0.1', 0, function() end)
+srv:close()
+test_run:wait_log('default', 'stopped', 1024, 0.01)