diff --git a/py/moduerrno.c b/py/moduerrno.c
index 826f52d2eaedeba07cb3ac4f488e41ba30e399f2..0b543a19347874476bb484f9f04a3838e968d71a 100644
--- a/py/moduerrno.c
+++ b/py/moduerrno.c
@@ -89,4 +89,13 @@ const mp_obj_module_t mp_module_uerrno = {
     .globals = (mp_obj_dict_t*)&mp_module_uerrno_globals,
 };
 
+mp_obj_t mp_errno_to_str(mp_obj_t errno_val) {
+    mp_map_elem_t *elem = mp_map_lookup((mp_map_t*)&errorcode_dict.map, errno_val, MP_MAP_LOOKUP);
+    if (elem == NULL) {
+        return errno_val;
+    } else {
+        return elem->value;
+    }
+}
+
 #endif //MICROPY_PY_UERRNO
diff --git a/py/mperrno.h b/py/mperrno.h
index da541284c6305c53a738438bf8955f3e689795a0..2dbb7b0a82db5789ecaf4f5b856ce9378fa9b85c 100644
--- a/py/mperrno.h
+++ b/py/mperrno.h
@@ -134,4 +134,10 @@
 
 #endif
 
+#if MICROPY_PY_UERRNO
+mp_obj_t mp_errno_to_str(mp_obj_t errno_val);
+#else
+static inline mp_obj_t mp_errno_to_str(mp_obj_t errno_val) { return errno_val; }
+#endif
+
 #endif // __MICROPY_INCLUDED_PY_MPERRNO_H__
diff --git a/py/objexcept.c b/py/objexcept.c
index adf17b08d0fa10474ea3b8a70e0ed52c7c73e337..4c1da1b387ab62e4f1ecf387ccf61a1e628fc2e5 100644
--- a/py/objexcept.c
+++ b/py/objexcept.c
@@ -36,6 +36,7 @@
 #include "py/objtype.h"
 #include "py/runtime.h"
 #include "py/gc.h"
+#include "py/mperrno.h"
 
 // Instance of MemoryError exception - needed by mp_malloc_fail
 const mp_obj_exception_t mp_const_MemoryError_obj = {{&mp_type_MemoryError}, 0, 0, NULL, (mp_obj_tuple_t*)&mp_const_empty_tuple_obj};
@@ -288,6 +289,10 @@ mp_obj_t mp_obj_new_exception(const mp_obj_type_t *exc_type) {
 
 // "Optimized" version for common(?) case of having 1 exception arg
 mp_obj_t mp_obj_new_exception_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) {
+    // try to provide a nice string instead of numeric value for errno's
+    if (exc_type == &mp_type_OSError && MP_OBJ_IS_SMALL_INT(arg)) {
+        arg = mp_errno_to_str(arg);
+    }
     return mp_obj_new_exception_args(exc_type, 1, &arg);
 }