[njs] Improved error messages for module loading failures.

noreply at nginx.com noreply at nginx.com
Tue Oct 22 01:07:02 UTC 2024


details:   https://github.com/nginx/njs/commit/ed36e94242de38e88a6cc536404609fc72bf6456
branches:  master
commit:    ed36e94242de38e88a6cc536404609fc72bf6456
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Fri, 18 Oct 2024 18:24:49 -0700
description:
Improved error messages for module loading failures.

There are several reasons why a file cannot be opened. Without
extra information, especially in containerized environments, these
problems are difficult to debug. Adding errno status to the
error output helps identify the root cause.

Additionally, error messages are now aligned between njs and QuickJS.

---
 nginx/ngx_js.c           | 350 ++++++++++++++++++++++++++++++++++++++++++++++-
 nginx/ngx_js.h           |   1 +
 src/njs.h                |   2 +
 src/njs_module.c         |   2 +-
 src/njs_parser.c         |   9 +-
 src/test/njs_unit_test.c |   4 +-
 test/shell_test_njs.exp  |   4 +-
 7 files changed, 362 insertions(+), 10 deletions(-)

diff --git a/nginx/ngx_js.c b/nginx/ngx_js.c
index 152045f0..f70288cf 100644
--- a/nginx/ngx_js.c
+++ b/nginx/ngx_js.c
@@ -1979,10 +1979,15 @@ ngx_qjs_module_loader(JSContext *cx, const char *module_name, void *opaque)
     info.name.start = (u_char *) module_name;
     info.name.length = njs_strlen(module_name);
 
+    errno = 0;
     ret = ngx_js_module_lookup(conf, &info);
     if (ret != NJS_OK) {
-        JS_ThrowReferenceError(cx, "could not load module filename '%s'",
-                               module_name);
+        if (errno != 0) {
+            JS_ThrowReferenceError(cx, "Cannot load module \"%s\" "
+                                   "(%s:%s)", module_name,
+                                   ngx_js_errno_string(errno), strerror(errno));
+        }
+
         return NULL;
     }
 
@@ -3764,8 +3769,14 @@ ngx_js_module_loader(njs_vm_t *vm, njs_external_ptr_t external, njs_str_t *name)
 
     info.name = *name;
 
+    errno = 0;
     ret = ngx_js_module_lookup(conf, &info);
     if (njs_slow_path(ret != NJS_OK)) {
+        if (errno != 0) {
+            njs_vm_ref_error(vm, "Cannot load module \"%V\" (%s:%s)", name,
+                             ngx_js_errno_string(errno), strerror(errno));
+        }
+
         return NULL;
     }
 
@@ -4076,6 +4087,341 @@ ngx_js_monotonic_time(void)
 }
 
 
+#define ngx_js_errno_case(e)                                                \
+    case e:                                                                 \
+        return #e;
+
+
+const char*
+ngx_js_errno_string(int errnum)
+{
+    switch (errnum) {
+#ifdef EACCES
+    ngx_js_errno_case(EACCES);
+#endif
+
+#ifdef EADDRINUSE
+    ngx_js_errno_case(EADDRINUSE);
+#endif
+
+#ifdef EADDRNOTAVAIL
+    ngx_js_errno_case(EADDRNOTAVAIL);
+#endif
+
+#ifdef EAFNOSUPPORT
+    ngx_js_errno_case(EAFNOSUPPORT);
+#endif
+
+#ifdef EAGAIN
+    ngx_js_errno_case(EAGAIN);
+#endif
+
+#ifdef EWOULDBLOCK
+#if EAGAIN != EWOULDBLOCK
+    ngx_js_errno_case(EWOULDBLOCK);
+#endif
+#endif
+
+#ifdef EALREADY
+    ngx_js_errno_case(EALREADY);
+#endif
+
+#ifdef EBADF
+    ngx_js_errno_case(EBADF);
+#endif
+
+#ifdef EBADMSG
+    ngx_js_errno_case(EBADMSG);
+#endif
+
+#ifdef EBUSY
+    ngx_js_errno_case(EBUSY);
+#endif
+
+#ifdef ECANCELED
+    ngx_js_errno_case(ECANCELED);
+#endif
+
+#ifdef ECHILD
+    ngx_js_errno_case(ECHILD);
+#endif
+
+#ifdef ECONNABORTED
+    ngx_js_errno_case(ECONNABORTED);
+#endif
+
+#ifdef ECONNREFUSED
+    ngx_js_errno_case(ECONNREFUSED);
+#endif
+
+#ifdef ECONNRESET
+    ngx_js_errno_case(ECONNRESET);
+#endif
+
+#ifdef EDEADLK
+    ngx_js_errno_case(EDEADLK);
+#endif
+
+#ifdef EDESTADDRREQ
+    ngx_js_errno_case(EDESTADDRREQ);
+#endif
+
+#ifdef EDOM
+    ngx_js_errno_case(EDOM);
+#endif
+
+#ifdef EDQUOT
+    ngx_js_errno_case(EDQUOT);
+#endif
+
+#ifdef EEXIST
+    ngx_js_errno_case(EEXIST);
+#endif
+
+#ifdef EFAULT
+    ngx_js_errno_case(EFAULT);
+#endif
+
+#ifdef EFBIG
+    ngx_js_errno_case(EFBIG);
+#endif
+
+#ifdef EHOSTUNREACH
+    ngx_js_errno_case(EHOSTUNREACH);
+#endif
+
+#ifdef EIDRM
+    ngx_js_errno_case(EIDRM);
+#endif
+
+#ifdef EILSEQ
+    ngx_js_errno_case(EILSEQ);
+#endif
+
+#ifdef EINPROGRESS
+    ngx_js_errno_case(EINPROGRESS);
+#endif
+
+#ifdef EINTR
+    ngx_js_errno_case(EINTR);
+#endif
+
+#ifdef EINVAL
+    ngx_js_errno_case(EINVAL);
+#endif
+
+#ifdef EIO
+    ngx_js_errno_case(EIO);
+#endif
+
+#ifdef EISCONN
+    ngx_js_errno_case(EISCONN);
+#endif
+
+#ifdef EISDIR
+    ngx_js_errno_case(EISDIR);
+#endif
+
+#ifdef ELOOP
+    ngx_js_errno_case(ELOOP);
+#endif
+
+#ifdef EMFILE
+    ngx_js_errno_case(EMFILE);
+#endif
+
+#ifdef EMLINK
+    ngx_js_errno_case(EMLINK);
+#endif
+
+#ifdef EMSGSIZE
+    ngx_js_errno_case(EMSGSIZE);
+#endif
+
+#ifdef EMULTIHOP
+    ngx_js_errno_case(EMULTIHOP);
+#endif
+
+#ifdef ENAMETOOLONG
+    ngx_js_errno_case(ENAMETOOLONG);
+#endif
+
+#ifdef ENETDOWN
+    ngx_js_errno_case(ENETDOWN);
+#endif
+
+#ifdef ENETRESET
+    ngx_js_errno_case(ENETRESET);
+#endif
+
+#ifdef ENETUNREACH
+    ngx_js_errno_case(ENETUNREACH);
+#endif
+
+#ifdef ENFILE
+    ngx_js_errno_case(ENFILE);
+#endif
+
+#ifdef ENOBUFS
+    ngx_js_errno_case(ENOBUFS);
+#endif
+
+#ifdef ENODATA
+    ngx_js_errno_case(ENODATA);
+#endif
+
+#ifdef ENODEV
+    ngx_js_errno_case(ENODEV);
+#endif
+
+#ifdef ENOENT
+    ngx_js_errno_case(ENOENT);
+#endif
+
+#ifdef ENOEXEC
+    ngx_js_errno_case(ENOEXEC);
+#endif
+
+#ifdef ENOLINK
+    ngx_js_errno_case(ENOLINK);
+#endif
+
+#ifdef ENOLCK
+#if ENOLINK != ENOLCK
+    ngx_js_errno_case(ENOLCK);
+#endif
+#endif
+
+#ifdef ENOMEM
+    ngx_js_errno_case(ENOMEM);
+#endif
+
+#ifdef ENOMSG
+    ngx_js_errno_case(ENOMSG);
+#endif
+
+#ifdef ENOPROTOOPT
+    ngx_js_errno_case(ENOPROTOOPT);
+#endif
+
+#ifdef ENOSPC
+    ngx_js_errno_case(ENOSPC);
+#endif
+
+#ifdef ENOSR
+    ngx_js_errno_case(ENOSR);
+#endif
+
+#ifdef ENOSTR
+    ngx_js_errno_case(ENOSTR);
+#endif
+
+#ifdef ENOSYS
+    ngx_js_errno_case(ENOSYS);
+#endif
+
+#ifdef ENOTCONN
+    ngx_js_errno_case(ENOTCONN);
+#endif
+
+#ifdef ENOTDIR
+    ngx_js_errno_case(ENOTDIR);
+#endif
+
+#ifdef ENOTEMPTY
+#if ENOTEMPTY != EEXIST
+    ngx_js_errno_case(ENOTEMPTY);
+#endif
+#endif
+
+#ifdef ENOTSOCK
+    ngx_js_errno_case(ENOTSOCK);
+#endif
+
+#ifdef ENOTSUP
+    ngx_js_errno_case(ENOTSUP);
+#else
+#ifdef EOPNOTSUPP
+    ngx_js_errno_case(EOPNOTSUPP);
+#endif
+#endif
+
+#ifdef ENOTTY
+    ngx_js_errno_case(ENOTTY);
+#endif
+
+#ifdef ENXIO
+    ngx_js_errno_case(ENXIO);
+#endif
+
+#ifdef EOVERFLOW
+    ngx_js_errno_case(EOVERFLOW);
+#endif
+
+#ifdef EPERM
+    ngx_js_errno_case(EPERM);
+#endif
+
+#ifdef EPIPE
+    ngx_js_errno_case(EPIPE);
+#endif
+
+#ifdef EPROTO
+    ngx_js_errno_case(EPROTO);
+#endif
+
+#ifdef EPROTONOSUPPORT
+    ngx_js_errno_case(EPROTONOSUPPORT);
+#endif
+
+#ifdef EPROTOTYPE
+    ngx_js_errno_case(EPROTOTYPE);
+#endif
+
+#ifdef ERANGE
+    ngx_js_errno_case(ERANGE);
+#endif
+
+#ifdef EROFS
+    ngx_js_errno_case(EROFS);
+#endif
+
+#ifdef ESPIPE
+    ngx_js_errno_case(ESPIPE);
+#endif
+
+#ifdef ESRCH
+    ngx_js_errno_case(ESRCH);
+#endif
+
+#ifdef ESTALE
+    ngx_js_errno_case(ESTALE);
+#endif
+
+#ifdef ETIME
+    ngx_js_errno_case(ETIME);
+#endif
+
+#ifdef ETIMEDOUT
+    ngx_js_errno_case(ETIMEDOUT);
+#endif
+
+#ifdef ETXTBSY
+    ngx_js_errno_case(ETXTBSY);
+#endif
+
+#ifdef EXDEV
+    ngx_js_errno_case(EXDEV);
+#endif
+
+    default:
+        break;
+    }
+
+    return "UNKNOWN CODE";
+}
+
+
 ngx_js_queue_t *
 ngx_js_queue_create(ngx_pool_t *pool, ngx_uint_t capacity)
 {
diff --git a/nginx/ngx_js.h b/nginx/ngx_js.h
index 8b6fbc85..8319dc85 100644
--- a/nginx/ngx_js.h
+++ b/nginx/ngx_js.h
@@ -412,6 +412,7 @@ njs_int_t ngx_js_ext_flags(njs_vm_t *vm, njs_object_prop_t *prop,
 
 ngx_int_t ngx_js_string(njs_vm_t *vm, njs_value_t *value, njs_str_t *str);
 ngx_int_t ngx_js_integer(njs_vm_t *vm, njs_value_t *value, ngx_int_t *n);
+const char *ngx_js_errno_string(int errnum);
 
 ngx_js_queue_t *ngx_js_queue_create(ngx_pool_t *pool, ngx_uint_t capacity);
 ngx_int_t ngx_js_queue_push(ngx_js_queue_t *queue, void *item);
diff --git a/src/njs.h b/src/njs.h
index 466932d8..14a245af 100644
--- a/src/njs.h
+++ b/src/njs.h
@@ -72,6 +72,8 @@ extern const njs_value_t            njs_value_undefined;
     njs_vm_error2(vm, 2, fmt, ##__VA_ARGS__)
 #define njs_vm_range_error(vm, fmt, ...)                                      \
     njs_vm_error2(vm, 3, fmt, ##__VA_ARGS__)
+#define njs_vm_ref_error(vm, fmt, ...)                                        \
+    njs_vm_error2(vm, 4, fmt, ##__VA_ARGS__)
 #define njs_vm_syntax_error(vm, fmt, ...)                                     \
     njs_vm_error2(vm, 5, fmt, ##__VA_ARGS__)
 #define njs_vm_type_error(vm, fmt, ...)                                       \
diff --git a/src/njs_module.c b/src/njs_module.c
index 859d96a8..5d9c96ae 100644
--- a/src/njs_module.c
+++ b/src/njs_module.c
@@ -148,7 +148,7 @@ njs_module_require(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
 
     module = njs_module_find(vm, &name, 0);
     if (njs_slow_path(module == NULL)) {
-        njs_error(vm, "Cannot find module \"%V\"", &name);
+        njs_error(vm, "Cannot load module \"%V\"", &name);
 
         return NJS_ERROR;
     }
diff --git a/src/njs_parser.c b/src/njs_parser.c
index 7eb6292e..1f16336f 100644
--- a/src/njs_parser.c
+++ b/src/njs_parser.c
@@ -8114,7 +8114,7 @@ njs_parser_module(njs_parser_t *parser, njs_str_t *name)
     vm = parser->vm;
 
     if (name->length == 0) {
-        njs_parser_syntax_error(parser, "Cannot find module \"%V\"", name);
+        njs_parser_ref_error(parser, "Cannot load module \"%V\"", name);
         return NULL;
     }
 
@@ -8124,13 +8124,16 @@ njs_parser_module(njs_parser_t *parser, njs_str_t *name)
     }
 
     if (vm->module_loader == NULL) {
-        njs_parser_syntax_error(parser, "Cannot load module \"%V\"", name);
+        njs_parser_ref_error(parser, "Module loader callback is not provided");
         return NULL;
     }
 
     module = vm->module_loader(vm, vm->module_loader_opaque, name);
     if (module == NULL) {
-        njs_parser_syntax_error(parser, "Cannot find module \"%V\"", name);
+        if (!njs_is_valid(&vm->exception)) {
+            njs_parser_ref_error(parser, "Cannot load module \"%V\"", name);
+        }
+
         return NULL;
     }
 
diff --git a/src/test/njs_unit_test.c b/src/test/njs_unit_test.c
index c52753a7..c4dc6dde 100644
--- a/src/test/njs_unit_test.c
+++ b/src/test/njs_unit_test.c
@@ -18975,7 +18975,7 @@ static njs_unit_test_t  njs_test[] =
     /* require(). */
 
     { njs_str("require('unknown_module')"),
-      njs_str("Error: Cannot find module \"unknown_module\"") },
+      njs_str("Error: Cannot load module \"unknown_module\"") },
 
     { njs_str("require()"),
       njs_str("TypeError: missing path") },
@@ -19045,7 +19045,7 @@ static njs_unit_test_t  njs_test[] =
       njs_str("SyntaxError: Unexpected token \"{\" in 1") },
 
     { njs_str("import x from ''"),
-      njs_str("SyntaxError: Cannot find module \"\" in 1") },
+      njs_str("ReferenceError: Cannot load module \"\" in 1") },
 
     { njs_str("export"),
       njs_str("SyntaxError: Illegal export statement in 1") },
diff --git a/test/shell_test_njs.exp b/test/shell_test_njs.exp
index 52ce4886..fac0fe3a 100644
--- a/test/shell_test_njs.exp
+++ b/test/shell_test_njs.exp
@@ -173,13 +173,13 @@ njs_test {
 # quiet mode
 
 njs_run {"-q" "test/js/import_chain.t.js"} \
-        "SyntaxError: Cannot find module \"lib2.js\" in 7"
+        "ReferenceError: Cannot load module \"lib2.js\" in 7"
 
 # sandboxing
 
 njs_test {
     {"var fs = require('fs')\r\n"
-     "Error: Cannot find module \"fs\"\r\n"}
+     "Error: Cannot load module \"fs\"\r\n"}
 } "-s"
 
 njs_test {


More information about the nginx-devel mailing list