Skip to content

Instantly share code, notes, and snippets.

@xeioex
Created February 2, 2026 16:52
Show Gist options
  • Select an option

  • Save xeioex/303d357c5b1d94462a1734fdb64200d3 to your computer and use it in GitHub Desktop.

Select an option

Save xeioex/303d357c5b1d94462a1734fdb64200d3 to your computer and use it in GitHub Desktop.
commit d5fae2e1ccc37de58573e4961958e82cf8d6d6b2
Author: Dmitry Volyntsev <[email protected]>
Date: Fri Jan 30 17:43:17 2026 -0800
Attaching JS stack trace for exceptions thrown by C code.
This fixes #1019 issue on Github.
diff --git a/nginx/t/js_fetch_error_stack.t b/nginx/t/js_fetch_error_stack.t
new file mode 100644
index 00000000..55183020
--- /dev/null
+++ b/nginx/t/js_fetch_error_stack.t
@@ -0,0 +1,111 @@
+#!/usr/bin/perl
+
+# (C) Dmitry Volyntsev
+# (C) F5, Inc.
+
+# Tests for http njs module, fetch method error stack traces.
+
+###############################################################################
+
+use warnings;
+use strict;
+
+use Test::More;
+
+BEGIN { use FindBin; chdir($FindBin::Bin); }
+
+use lib 'lib';
+use Test::Nginx;
+
+###############################################################################
+
+select STDERR; $| = 1;
+select STDOUT; $| = 1;
+
+my $t = Test::Nginx->new()->has(qw/http/)
+ ->write_file_expand('nginx.conf', <<'EOF');
+
+%%TEST_GLOBALS%%
+
+daemon off;
+
+events {
+}
+
+http {
+ %%TEST_GLOBALS_HTTP%%
+
+ js_import test.js;
+
+ server {
+ listen 127.0.0.1:8080;
+ server_name localhost;
+
+ location /testAsync {
+ js_content test.testAsync;
+ }
+
+ location /testSync {
+ js_content test.testSync;
+ }
+
+ location /testAsyncThrow {
+ js_content test.testAsyncThrow;
+ }
+ }
+}
+
+EOF
+
+$t->write_file('test.js', <<'EOF');
+ async function testAsync(r) {
+ try {
+ await ngx.fetch('http://127.0.0.1:12345');
+
+ } catch(e) {
+ r.return(200, `stack: ${e.stack}\n`);
+ }
+ }
+
+ function testSync(r) {
+ try {
+ throw Error('oops');
+
+ } catch(e) {
+ r.return(200, `stack: ${e.stack}\n`);
+ }
+ }
+
+ async function testAsyncThrow(r) {
+ try {
+ throw Error('oops');
+
+ } catch(e) {
+ r.return(200, `stack: ${e.stack}\n`);
+ }
+ }
+
+ export default { testAsync, testSync, testAsyncThrow }
+EOF
+
+$t->try_run('no njs');
+
+$t->plan(6);
+
+###############################################################################
+
+like(http_get('/testSync'), qr/stack: Error: oops/s, 'sync stack exists');
+like(http_get('/testSync'), qr/at testSync \([^)]*test\.js:12\)/s,
+ 'sync stack line number');
+
+like(http_get('/testAsync'), qr/stack: Error: connect failed/s,
+ 'async stack exists');
+like(http_get('/testAsync'), qr/at testAsync \([^)]*test\.js:3\)/s,
+ 'async stack line number');
+
+like(http_get('/testAsyncThrow'), qr/stack: Error: oops/s,
+ 'async throw stack exists');
+like(http_get('/testAsyncThrow'), qr/at testAsyncThrow \([^)]*test\.js:21\)/s,
+ 'async throw stack line number');
+
+###############################################################################
diff --git a/src/njs_async.c b/src/njs_async.c
index c352e86e..bc4769f4 100644
--- a/src/njs_async.c
+++ b/src/njs_async.c
@@ -82,6 +82,13 @@ njs_await_fulfilled(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
vm->active_frame = async_frame;
if (exception) {
+ /*
+ * Attaching JS stack trace for exceptions thrown by
+ * C code (for example by njs_vm_error3()), in a C call stack.
+ */
+ async->pc = ctx->await_pc;
+ njs_error_stack_attach(vm, *value, 0);
+
njs_vm_throw(vm, value);
} else {
diff --git a/src/njs_async.h b/src/njs_async.h
index 3addb2d6..8e4111f6 100644
--- a/src/njs_async.h
+++ b/src/njs_async.h
@@ -13,6 +13,7 @@ typedef struct {
njs_frame_t *await;
uintptr_t index;
u_char *pc;
+ u_char *await_pc;
} njs_async_ctx_t;
diff --git a/src/njs_vmcode.c b/src/njs_vmcode.c
index 6d991042..7171b996 100644
--- a/src/njs_vmcode.c
+++ b/src/njs_vmcode.c
@@ -2772,6 +2772,7 @@ njs_vmcode_await(njs_vm_t *vm, njs_vmcode_await_t *await,
}
ctx->pc = (u_char *) await + sizeof(njs_vmcode_await_t);
+ ctx->await_pc = (u_char *) await;
ctx->index = await->retval;
frame = (njs_frame_t *) active;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment