diff --git a/unix/unix_mphal.c b/unix/unix_mphal.c
index 9da86805622397b736068d4bc64789a4e3d79b2f..67c818042ae0e2a8821a8d29791b2ab0cd538020 100644
--- a/unix/unix_mphal.c
+++ b/unix/unix_mphal.c
@@ -108,23 +108,45 @@ void mp_hal_stdio_mode_orig(void) {
 
 #endif
 
-int mp_hal_stdin_rx_chr(void) {
-    unsigned char c;
-    #if MICROPY_PY_OS_DUPTERM
-    while (MP_STATE_PORT(term_obj) != MP_OBJ_NULL) {
+static int call_dupterm_read(void) {
+    nlr_buf_t nlr;
+    if (nlr_push(&nlr) == 0) {
         mp_obj_t read_m[3];
         mp_load_method(MP_STATE_PORT(term_obj), MP_QSTR_read, read_m);
         read_m[2] = MP_OBJ_NEW_SMALL_INT(1);
         mp_obj_t res = mp_call_method_n_kw(1, 0, read_m);
         if (res == mp_const_none) {
-            break;
+            return -1;
         }
         mp_buffer_info_t bufinfo;
         mp_get_buffer_raise(res, &bufinfo, MP_BUFFER_READ);
         if (bufinfo.len == 0) {
+            mp_printf(&mp_plat_print, "dupterm: EOF received, deactivating\n");
+            MP_STATE_PORT(term_obj) = NULL;
+            return -1;
+        }
+        nlr_pop();
+        return *(byte*)bufinfo.buf;
+    } else {
+        // Temporarily disable dupterm to avoid infinite recursion
+        mp_obj_t save_term = MP_STATE_PORT(term_obj);
+        MP_STATE_PORT(term_obj) = NULL;
+        mp_printf(&mp_plat_print, "dupterm: ");
+        mp_obj_print_exception(&mp_plat_print, nlr.ret_val);
+        MP_STATE_PORT(term_obj) = save_term;
+    }
+
+    return -1;
+}
+
+int mp_hal_stdin_rx_chr(void) {
+    unsigned char c;
+    #if MICROPY_PY_OS_DUPTERM
+    while (MP_STATE_PORT(term_obj) != MP_OBJ_NULL) {
+        int c = call_dupterm_read();
+        if (c == -1) {
             break;
         }
-        c = *(byte*)bufinfo.buf;
         if (c == '\n') {
             c = '\r';
         }