diff --git a/py/compile.c b/py/compile.c
index 42222528e2b6c289c6310689b1880931f45cd075..ca01d74785b2a86f0453e9f9b45cd56f2475668b 100644
--- a/py/compile.c
+++ b/py/compile.c
@@ -1599,7 +1599,6 @@ STATIC void compile_try_except(compiler_t *comp, mp_parse_node_t pn_body, int n_
         }
         compile_node(comp, pns_except->nodes[1]); // the <body>
         if (qstr_exception_local != 0) {
-            EMIT(pop_block);
             EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
             EMIT_ARG(label_assign, l3);
             EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
@@ -1635,7 +1634,6 @@ STATIC void compile_try_finally(compiler_t *comp, mp_parse_node_t pn_body, int n
     } else {
         compile_try_except(comp, pn_body, n_except, pn_except, pn_else);
     }
-    EMIT(pop_block);
     EMIT_ARG(load_const_tok, MP_TOKEN_KW_NONE);
     EMIT_ARG(label_assign, l_finally_block);
     compile_node(comp, pn_finally);
@@ -1811,8 +1809,7 @@ STATIC void compile_async_with_stmt_helper(compiler_t *comp, int n, mp_parse_nod
         compile_async_with_stmt_helper(comp, n - 1, nodes + 1, body);
         EMIT_ARG(adjust_stack_size, -3);
 
-        // Finish the "try" block
-        EMIT(pop_block);
+        // We have now finished the "try" block and fall through to the "finally"
 
         // At this point, after the with body has executed, we have 3 cases:
         // 1. no exception, we just fall through to this point; stack: (..., ctx_mgr)
diff --git a/py/emitbc.c b/py/emitbc.c
index 6a46cfb5937ded9ce86a906d36a31b49c73d5131..65d6509051092d70a5482bb38d1626fa432b74b3 100644
--- a/py/emitbc.c
+++ b/py/emitbc.c
@@ -738,7 +738,6 @@ void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind) {
 }
 
 void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label) {
-    mp_emit_bc_pop_block(emit);
     mp_emit_bc_load_const_tok(emit, MP_TOKEN_KW_NONE);
     mp_emit_bc_label_assign(emit, label);
     emit_bc_pre(emit, 2); // ensure we have enough stack space to call the __exit__ method
diff --git a/py/vm.c b/py/vm.c
index a0ee2e89a40f31c3a21e79f68d3df7509fe16101..56494dfa1435c9279e8e7ef7bbe545fda6afa58f 100644
--- a/py/vm.c
+++ b/py/vm.c
@@ -633,8 +633,6 @@ dispatch_loop:
                             // replacing it with None, which signals END_FINALLY to just
                             // execute the finally handler normally.
                             SET_TOP(mp_const_none);
-                            assert(exc_sp >= exc_stack);
-                            POP_EXC_BLOCK();
                         } else {
                             // We need to re-raise the exception.  We pop __exit__ handler
                             // by copying the exception instance down to the new top-of-stack.
@@ -654,7 +652,7 @@ unwind_jump:;
                     while ((unum & 0x7f) > 0) {
                         unum -= 1;
                         assert(exc_sp >= exc_stack);
-                        if (MP_TAGPTR_TAG1(exc_sp->val_sp)) {
+                        if (MP_TAGPTR_TAG1(exc_sp->val_sp) && exc_sp->handler > ip) {
                             // Getting here the stack looks like:
                             //     (..., X, dest_ip)
                             // where X is pointed to by exc_sp->val_sp and in the case
@@ -698,6 +696,8 @@ unwind_jump:;
                     // if TOS is an integer, finishes coroutine and returns control to caller
                     // if TOS is an exception, reraises the exception
                     if (TOP() == mp_const_none) {
+                        assert(exc_sp >= exc_stack);
+                        POP_EXC_BLOCK();
                         sp--;
                     } else if (mp_obj_is_small_int(TOP())) {
                         // We finished "finally" coroutine and now dispatch back
@@ -1063,7 +1063,7 @@ unwind_jump:;
 unwind_return:
                     // Search for and execute finally handlers that aren't already active
                     while (exc_sp >= exc_stack) {
-                        if (!currently_in_except_block && MP_TAGPTR_TAG1(exc_sp->val_sp)) {
+                        if (!currently_in_except_block && MP_TAGPTR_TAG1(exc_sp->val_sp) && exc_sp->handler > ip) {
                             // Found a finally handler that isn't active.
                             // Getting here the stack looks like:
                             //     (..., X, [iter0, iter1, ...,] ret_val)
@@ -1419,7 +1419,7 @@ unwind_loop:
                 mp_obj_exception_add_traceback(MP_OBJ_FROM_PTR(nlr.ret_val), source_file, source_line, block_name);
             }
 
-            while (currently_in_except_block) {
+            while (currently_in_except_block || (exc_sp >= exc_stack && exc_sp->handler <= code_state->ip)) {
                 // nested exception
 
                 assert(exc_sp >= exc_stack);
diff --git a/tests/basics/try_finally_break.py b/tests/basics/try_finally_break.py
new file mode 100644
index 0000000000000000000000000000000000000000..ae7226637d72ba96c7eb897e3c8933cb9b6c86c8
--- /dev/null
+++ b/tests/basics/try_finally_break.py
@@ -0,0 +1,99 @@
+# test break within (nested) finally
+
+# basic case with break in finally
+def f():
+    for _ in range(2):
+        print(1)
+        try:
+            pass
+        finally:
+            print(2)
+            break
+            print(3)
+        print(4)
+    print(5)
+f()
+
+# where the finally swallows an exception
+def f():
+    lst = [1, 2, 3]
+    for x in lst:
+        print('a', x)
+        try:
+            raise Exception
+        finally:
+            print(1)
+            break
+        print('b', x)
+f()
+
+# basic nested finally with break in inner finally
+def f():
+    for i in range(2):
+        print('iter', i)
+        try:
+            raise TypeError
+        finally:
+            print(1)
+            try:
+                raise ValueError
+            finally:
+                break
+print(f())
+
+# similar to above but more nesting
+def f():
+    for i in range(2):
+        try:
+            raise ValueError
+        finally:
+            print(1)
+            try:
+                raise TypeError
+            finally:
+                print(2)
+                try:
+                    pass
+                finally:
+                    break
+print(f())
+
+# lots of nesting
+def f():
+    for i in range(2):
+        try:
+            raise ValueError
+        finally:
+            print(1)
+            try:
+                raise TypeError
+            finally:
+                print(2)
+                try:
+                    raise Exception
+                finally:
+                    break
+print(f())
+
+# basic case combined with try-else
+def f(arg):
+    for _ in range(2):
+        print(1)
+        try:
+            if arg == 1:
+                raise ValueError
+            elif arg == 2:
+                raise TypeError
+        except ValueError:
+            print(2)
+        else:
+            print(3)
+        finally:
+            print(4)
+            break
+            print(5)
+        print(6)
+    print(7)
+f(0) # no exception, else should execute
+f(1) # exception caught, else should be skipped
+f(2) # exception not caught, finally swallows exception, else should be skipped
diff --git a/tests/basics/try_return.py b/tests/basics/try_return.py
index 492c18d95c3c0df2ad52136192e9ab6e36ed23cf..a24290c4fb7a9aaf397103ed6ddc461e26ad62ed 100644
--- a/tests/basics/try_return.py
+++ b/tests/basics/try_return.py
@@ -1,5 +1,14 @@
 # test use of return with try-except
 
+def f():
+    try:
+        print(1)
+        return
+    except:
+        print(2)
+    print(3)
+f()
+
 def f(l, i):
     try:
         return l[i]
diff --git a/tests/cmdline/cmd_showbc.py.exp b/tests/cmdline/cmd_showbc.py.exp
index 1274cda00f59e16ca870016cb7a46860adacb829..d119a6198b47b26385572f58733120d8e3e6eb4f 100644
--- a/tests/cmdline/cmd_showbc.py.exp
+++ b/tests/cmdline/cmd_showbc.py.exp
@@ -265,7 +265,6 @@ Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+):
 \\d\+ POP_EXCEPT
 \\d\+ JUMP \\d\+
 \\d\+ END_FINALLY
-\\d\+ POP_BLOCK
 \\d\+ LOAD_CONST_NONE
 \\d\+ LOAD_FAST 1
 \\d\+ POP_TOP
@@ -286,7 +285,6 @@ Raw bytecode (code_info_size=\\d\+, bytecode_size=\\d\+):
 \\d\+ POP_TOP
 \\d\+ LOAD_DEREF 14
 \\d\+ POP_TOP
-\\d\+ POP_BLOCK
 \\d\+ LOAD_CONST_NONE
 \\d\+ WITH_CLEANUP
 \\d\+ END_FINALLY