Issue #14417: Mutating a dict during lookup now restarts the lookup instead of raising a RuntimeError (undoes issue #14205).
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 212fd5e..a34b1cf 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -2210,10 +2210,6 @@
       See :class:`collections.Counter` for a complete implementation including
       other methods helpful for accumulating and managing tallies.
 
-      .. versionchanged:: 3.3
-         If the dict is modified during the lookup, a :exc:`RuntimeError`
-         exception is now raised.
-
    .. describe:: d[key] = value
 
       Set ``d[key]`` to *value*.
diff --git a/Doc/whatsnew/3.3.rst b/Doc/whatsnew/3.3.rst
index 35920b9..148324e 100644
--- a/Doc/whatsnew/3.3.rst
+++ b/Doc/whatsnew/3.3.rst
@@ -557,13 +557,6 @@
 
   (:issue:`12170`)
 
-* A dict lookup now raises a :exc:`RuntimeError` if the dict is modified during
-  the lookup. If you implement your own comparison function for objects used
-  as dict keys and the dict is shared by multiple threads, access to the dict
-  should be protected by a lock.
-
-  (:issue:`14205`)
-
 * New methods have been added to :class:`list` and :class:`bytearray`:
   ``copy()`` and ``clear()``.
 
diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py
index 426f76e..dd4d552 100644
--- a/Lib/test/test_dict.py
+++ b/Lib/test/test_dict.py
@@ -411,7 +411,7 @@
                 d[i+1] = 1
 
     def test_mutating_lookup(self):
-        # changing dict during a lookup
+        # changing dict during a lookup (issue #14417)
         class NastyKey:
             mutate_dict = None
 
@@ -433,9 +433,8 @@
         key2 = NastyKey(2)
         d = {key1: 1}
         NastyKey.mutate_dict = (d, key1)
-        with self.assertRaisesRegex(RuntimeError,
-                                    'dictionary changed size during lookup'):
-            d[key2] = 2
+        d[key2] = 2
+        self.assertEqual(d, {key2: 2})
 
     def test_repr(self):
         d = {}
diff --git a/Misc/NEWS b/Misc/NEWS
index 031738d..f27dab5 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,9 @@
 Core and Builtins
 -----------------
 
+- Issue #14417: Mutating a dict during lookup now restarts the lookup instead
+  of raising a RuntimeError (undoes issue #14205).
+
 - Issue #14738: Speed-up UTF-8 decoding on non-ASCII data.  Patch by Serhiy
   Storchaka.
 
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 077f3cd..fd1d46c 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -439,12 +439,15 @@
     register size_t i;
     register size_t perturb;
     register PyDictKeyEntry *freeslot;
-    register size_t mask = DK_MASK(mp->ma_keys);
-    PyDictKeyEntry *ep0 = &mp->ma_keys->dk_entries[0];
+    register size_t mask;
+    PyDictKeyEntry *ep0;
     register PyDictKeyEntry *ep;
     register int cmp;
     PyObject *startkey;
 
+top:
+    mask = DK_MASK(mp->ma_keys);
+    ep0 = &mp->ma_keys->dk_entries[0];
     i = (size_t)hash & mask;
     ep = &ep0[i];
     if (ep->me_key == NULL || ep->me_key == key) {
@@ -468,9 +471,8 @@
                 }
             }
             else {
-                PyErr_SetString(PyExc_RuntimeError,
-                                "dictionary changed size during lookup");
-                return NULL;
+                /* The dict was mutated, restart */
+                goto top;
             }
         }
         freeslot = NULL;
@@ -510,9 +512,8 @@
                 }
             }
             else {
-                PyErr_SetString(PyExc_RuntimeError,
-                                "dictionary changed size during lookup");
-                return NULL;
+                /* The dict was mutated, restart */
+                goto top;
             }
         }
         else if (ep->me_key == dummy && freeslot == NULL)