diff --git a/include/opendht/network_engine.h b/include/opendht/network_engine.h
index fa32a822354b779db23a875d0f50734919c009c4..af526c5101fb0da85d69152e44dbfd8543006a20 100644
--- a/include/opendht/network_engine.h
+++ b/include/opendht/network_engine.h
@@ -400,8 +400,11 @@ private:
     bool isNodeBlacklisted(const sockaddr*, socklen_t) const;
 
     void requestStep(std::shared_ptr<Request> req) {
-        if (not req->pending())
+        if (not req->pending()) {
+            if (req->cancelled())
+                requests.erase(req->tid);
             return;
+        }
 
         auto now = scheduler.time();
         if (req->isExpired(now)) {
diff --git a/include/opendht/request.h b/include/opendht/request.h
index edf3a62de208a459bbd28d63fcb88a947300d847..c4e1104190f0ebe22384c81526f49ba0b5573a49 100644
--- a/include/opendht/request.h
+++ b/include/opendht/request.h
@@ -75,12 +75,6 @@ struct Request {
                 clear();
         }
     }
-private:
-    static const constexpr size_t MAX_ATTEMPT_COUNT {3};
-
-    bool isExpired(time_point now) const {
-        return pending() and now > last_try + Node::MAX_RESPONSE_TIME and attempt_count >= Request::MAX_ATTEMPT_COUNT;
-    }
 
     void cancel() {
         if (pending()) {
@@ -89,6 +83,13 @@ private:
         }
     }
 
+private:
+    static const constexpr size_t MAX_ATTEMPT_COUNT {3};
+
+    bool isExpired(time_point now) const {
+        return pending() and now > last_try + Node::MAX_RESPONSE_TIME and attempt_count >= Request::MAX_ATTEMPT_COUNT;
+    }
+
     void clear() {
         on_done = {};
         on_expired = {};
diff --git a/src/network_engine.cpp b/src/network_engine.cpp
index 254be1c29cf994786b622277a050b678406fce78..4c08830f5319c3bad978d69c662862e52c5408e7 100644
--- a/src/network_engine.cpp
+++ b/src/network_engine.cpp
@@ -273,13 +273,14 @@ NetworkEngine::processMessage(const uint8_t *buf, size_t buflen, const sockaddr*
         } else
             node->update(from, fromlen);        
 
-        if (req->cancelled()) {
-            DHT_LOG.ERROR("Request is cancelled: %d", msg.tid);
+        onNewNode(node, 2);
+        onReportedAddr(msg.id, (sockaddr*)&msg.addr.first, msg.addr.second);
+
+        if (req->cancelled() or req->expired() or (req->completed() and not req->persistent)) {
+            requests.erase(reqp);
             return;
         }
 
-        onNewNode(node, 2);
-        onReportedAddr(msg.id, (sockaddr*)&msg.addr.first, msg.addr.second);
         switch (msg.type) {
         case MessageType::Error: {
             if (msg.error_code == DhtProtocolException::UNAUTHORIZED