diff --git a/py/nlr.h b/py/nlr.h
index ce62334fd43c5619f083521866957603af6b1631..4e931919dbbfb5e5eebe60b43c2cc442eb34f62f 100644
--- a/py/nlr.h
+++ b/py/nlr.h
@@ -27,6 +27,11 @@ unsigned int nlr_push(nlr_buf_t *);
 void nlr_pop(void);
 void nlr_jump(void *val) __attribute__((noreturn));
 
+// This must be implemented by a port.  It's called by nlr_jump
+// if no nlr buf has been pushed.  It must not return, but rather
+// should bail out with a fatal error.
+void nlr_jump_fail(void *val);
+
 // use nlr_raise instead of nlr_jump so that debugging is easier
 #ifndef DEBUG
 #define nlr_raise(val) nlr_jump(val)
diff --git a/py/nlrthumb.S b/py/nlrthumb.S
index 8c6eb95522fa8e2f5fe0f6167ec6196a73826a05..6a0c7ee1e1d99d61bc2bb3e0c8ec018a06643221 100644
--- a/py/nlrthumb.S
+++ b/py/nlrthumb.S
@@ -60,6 +60,8 @@ nlr_pop:
 nlr_jump:
     ldr     r3, .L2                 @ load addr of nlr_top
     ldr     r2, [r3]                @ load nlr_top
+    cmp     r2, #0                  @ test if nlr_top is NULL
+    beq     nlr_jump_fail           @ if nlr_top is NULL, transfer control to nlr_jump_fail
     str     r0, [r2, #4]            @ store return value
     ldr     r0, [r2]                @ load prev nlr_buf
     str     r0, [r3]                @ store prev nol_buf into nlr_top (to unlink list)
diff --git a/py/nlrx64.S b/py/nlrx64.S
index a4073981a58d56fb0b7a895410bfc19d7d7ef3e9..929a348caf23309b8191d2668b07c87e0a0d228d 100644
--- a/py/nlrx64.S
+++ b/py/nlrx64.S
@@ -61,6 +61,8 @@ nlr_jump:
 #endif
     movq    %rdi, %rax              # put return value in %rax
     movq    nlr_top(%rip), %rdi     # get nlr_top into %rdi
+    test    %rdi, %rdi              # check for nlr_top being NULL
+    je      .fail                   # fail if nlr_top is NULL
     movq    %rax, 8(%rdi)           # store return value
     movq    (%rdi), %rax            # load prev nlr_buf
     movq    %rax, nlr_top(%rip)     # store prev nlr_buf (to unlink list)
@@ -76,10 +78,14 @@ nlr_jump:
     xorq    %rax, %rax              # clear return register
     inc     %al                     # increase to make 1, non-local return
     ret                             # return
+.fail:
+    movq    %rax, %rdi              # put argument back in first-arg register
+    je      nlr_jump_fail           # transfer control to nlr_jump_fail
 #if !(defined(__APPLE__) && defined(__MACH__))
     .size   nlr_jump, .-nlr_jump
 #endif
 
+    .bss
 #if !(defined(__APPLE__) && defined(__MACH__))
     .local  nlr_top
 #endif
@@ -107,12 +113,21 @@ nlr_push:
     xorq    %rax, %rax              # return 0, normal return
     ret                             # return
 
-/* void nlr_jump(rcx=uint val) */
+/* void nlr_pop() */
+    .globl  nlr_pop
+nlr_pop:
+    movq    nlr_top(%rip), %rax     # get nlr_top into %rax
+    movq    (%rax), %rax            # load prev nlr_buf
+    movq    %rax, nlr_top(%rip)     # store prev nlr_buf (to unlink list)
+    ret                             # return
 
+/* void nlr_jump(rcx=uint val) */
     .globl  nlr_jump
 nlr_jump:
     movq    %rcx, %rax              # put return value in %rax
-    movq    nlr_top(%rip), %rcx     # get nlr_top into %rsi
+    movq    nlr_top(%rip), %rcx     # get nlr_top into %rcx
+    test    %rcx, %rcx              # check for nlr_top being NULL
+    je      .fail                   # fail if nlr_top is NULL
     movq    %rax, 8(%rcx)           # store return value
     movq    (%rcx), %rax            # load prev nlr_buf
     movq    %rax, nlr_top(%rip)     # store prev nlr_buf (to unlink list)
@@ -130,17 +145,13 @@ nlr_jump:
     xorq    %rax, %rax              # clear return register
     inc     %al                     # increase to make 1, non-local return
     ret                             # return
+.fail:
+    movq    %rax, %rcx              # put argument back in first-arg register
+    je      nlr_jump_fail           # transfer control to nlr_jump_fail
 
+    .bss
     .comm   nlr_top,8,8
 
-    /* void nlr_pop() */
-    .globl  nlr_pop
-nlr_pop:
-    movq    nlr_top(%rip), %rax     # get nlr_top into %rax
-    movq    (%rax), %rax            # load prev nlr_buf
-    movq    %rax, nlr_top(%rip)     # store prev nlr_buf (to unlink list)
-    ret                             # return
-
 #endif // !defined(__CYGWIN__)
 
 #endif // __x86_64__
diff --git a/py/nlrx86.S b/py/nlrx86.S
index 5cfd4a8cfccb842543837c488d15450fc8be6fb5..003de5095fdf9fba341f886165632a2859381396 100644
--- a/py/nlrx86.S
+++ b/py/nlrx86.S
@@ -60,6 +60,8 @@ _nlr_jump:
 nlr_jump:
 #endif
     mov     nlr_top, %edx           # load nlr_top
+    test    %edx, %edx              # check for nlr_top being NULL
+    je      nlr_jump_fail           # fail if nlr_top is NULL
     mov     4(%esp), %eax           # load return value
     mov     %eax, 4(%edx)           # store return value
     mov     (%edx), %eax            # load prev nlr_top
@@ -78,6 +80,7 @@ nlr_jump:
     .size   nlr_jump, .-nlr_jump
 #endif
 
+    .bss
 #ifndef _WIN32
     .local  nlr_top
 #endif
diff --git a/stm/main.c b/stm/main.c
index a11a813d2dadf5a4189cdf466ddaab126713e1df..636f900212957cef6f0920b3df0ee8bb46194db4 100644
--- a/stm/main.c
+++ b/stm/main.c
@@ -79,6 +79,11 @@ void __fatal_error(const char *msg) {
     }
 }
 
+void nlr_jump_fail(void *val) {
+    printf("FATAL: uncaught exception %p\n", val);
+    __fatal_error("");
+}
+
 STATIC mp_obj_t pyb_config_source_dir = MP_OBJ_NULL;
 STATIC mp_obj_t pyb_config_main = MP_OBJ_NULL;
 
diff --git a/stmhal/main.c b/stmhal/main.c
index 897271d14a8d9e418412fbdf65a7fe45c02a2274..e5ba67dc1d9af37a69de4a295dd45e05a79eefdb 100644
--- a/stmhal/main.c
+++ b/stmhal/main.c
@@ -73,6 +73,11 @@ void __fatal_error(const char *msg) {
     }
 }
 
+void nlr_jump_fail(void *val) {
+    printf("FATAL: uncaught exception %p\n", val);
+    __fatal_error("");
+}
+
 STATIC mp_obj_t pyb_config_source_dir = MP_OBJ_NULL;
 STATIC mp_obj_t pyb_config_main = MP_OBJ_NULL;
 STATIC mp_obj_t pyb_config_usb_mode = MP_OBJ_NULL;
diff --git a/unix-cpy/main.c b/unix-cpy/main.c
index 944d26e9f3ec060d03fb3b05b7a4d11f8a649b0c..6cdd72c5da8f78e5255b6f045a24735b7986292e 100644
--- a/unix-cpy/main.c
+++ b/unix-cpy/main.c
@@ -1,3 +1,4 @@
+#include <stdlib.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -74,12 +75,11 @@ int main(int argc, char **argv) {
     return 0;
 }
 
-// for sqrt
-#include <math.h>
-machine_float_t machine_sqrt(machine_float_t x) {
-    return sqrt(x);
-}
-
 mp_import_stat_t mp_import_stat(const char *path) {
     return MP_IMPORT_STAT_NO_EXIST;
 }
+
+void nlr_jump_fail(void *val) {
+    printf("FATAL: uncaught NLR %p\n", val);
+    exit(1);
+}
diff --git a/unix/main.c b/unix/main.c
index 11df4cadf29fe1da73045bd8727729bddbef8637..8065663011eb1a0c7a32ec55ee52b8bc75874bff 100644
--- a/unix/main.c
+++ b/unix/main.c
@@ -293,6 +293,7 @@ void pre_process_options(int argc, char **argv) {
 }
 
 int main(int argc, char **argv) {
+    nlr_jump(0);
     volatile int stack_dummy;
     stack_top = (void*)&stack_dummy;
 
@@ -447,3 +448,8 @@ int DEBUG_printf(const char *fmt, ...) {
     va_end(ap);
     return ret;
 }
+
+void nlr_jump_fail(void *val) {
+    printf("FATAL: uncaught NLR %p\n", val);
+    exit(1);
+}