NodeJS fails on `uv_thread_join` with thread sanitizers
Maintainers usually reply within 1 day
@SankeerthNara is already working on this.
Since Aug 13, 2026.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Needs clarification
- Activity status
- Quiet
- Domain
- backend, testing-qa
Research direction
Start by running metacall-node-python-callback-testd with the reported TSAN_OPTIONS and compare the failure with the trace from loader/source/loader_impl.c:811 and metacall_node_python_callback_test.cpp:51. Review the sanitizer configuration and callback test setup; done means identifying and resolving the uv_thread_join/TLS or reported Node.js race without introducing sanitizer failures.
Written by the indexing model from the issue text.
Description
🐛 Bug Report
Apparently it's happening during the destruction of the TLS.
I could get more by adding the following flags to TSAN:
set(TESTS_SANITIZER_ENVIRONMENT_VARIABLES
"TSAN_OPTIONS=verbosity=2:halt_on_error=1:history_size=7:report_signal_unsafe=1:report_thread_leaks=1:second_deadlock_stack=1:symbolize=1:suppressions=${CMAKE_SOURCE_DIR}/source/tests/sanitizer/tsan.supp"
)
Then I got this:
==24215==__tls_get_addr: DTLS_Find 0x7f5445772690 6
==24215==__tls_get_addr: DTLS_Find 0x7f5445772690 6
==24215==__tls_get_addr: DTLS_Find 0x7f5445772690 6
==24215==__tls_get_addr: DTLS_Find 0x7f5445772690 6
==24215==__tls_get_addr: DTLS_Find 0x7f5445772690 6
==24215==__tls_get_addr: DTLS_Find 0x7f5445772690 6
==24215==__tls_get_addr: DTLS_Destroy 0x7f5445772690
==24215==__tls_get_addr: DTLS_Deallocate 0x7f544ee00000
ThreadSanitizer: CHECK failed: tsan_interceptors_posix.cpp:2058 "((thr->slot)) != (0)" (0x0, 0x0) (tid=24217)
Then running with strace got me the real error (apparently):
export PY_PORT_LIBRARY_PATH=/usr/local/metacall/source/ports/py_port
strace -f -e trace=exit,exit_group,rt_sigaction,rt_sigprocmask ./metacall-node-python-callback-testd
This is the real error:
WARNING: ThreadSanitizer: data race (pid=24227)
Write of size 1 at 0x723000027cf8 by thread T1 (mutexes: write M0, write M1, write M2):
#0 pthread_mutex_destroy ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1332 (libtsan.so.2+0x52bcc) (BuildId: 99ef88596cb10a8ccf307fe1a9070f66a44b1624)
#1 v8::platform::DefaultJobState::~DefaultJobState() <null> (libnode.so.115+0xfd822c) (BuildId: 9cae2928171fe8cd59431ad6df3e33dc66743b19)
Previous atomic read of size 1 at 0x723000027cf8 by thread T6:
#0 pthread_mutex_lock ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1341 (libtsan.so.2+0x558ce) (BuildId: 99ef88596cb10a8ccf307fe1a9070f66a44b1624)
#1 v8::platform::DefaultJobState::CanRunFirstTask() <null> (libnode.so.115+0xfd855b) (BuildId: 9cae2928171fe8cd59431ad6df3e33dc66743b19)
Location is heap block of size 192 at 0x723000027cc0 allocated by thread T1:
#0 operator new(unsigned long) ../../../../src/libsanitizer/tsan/tsan_new_delete.cpp:64 (libtsan.so.2+0x9c506) (BuildId: 99ef88596cb10a8ccf307fe1a9070f66a44b1624)
#1 v8::platform::NewDefaultJobHandle(v8::Platform*, v8::TaskPriority, std::unique_ptr<v8::JobTask, std::default_delete<v8::JobTask> >, unsigned long) <null> (libnode.so.115+0xfd9f2a) (BuildId: 9cae2928171fe8cd59431ad6df3e33dc66743b19)
Mutex M0 (0x72140000a0a0) created at:
#0 pthread_mutex_init ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1315 (libtsan.so.2+0x5545f) (BuildId: 99ef88596cb10a8ccf307fe1a9070f66a44b1624)
#1 v8::internal::ThreadManager::ThreadManager(v8::internal::Isolate*) <null> (libnode.so.115+0x1189221) (BuildId: 9cae2928171fe8cd59431ad6df3e33dc66743b19)
Mutex M1 (0x7238000070a0) created at:
#0 pthread_mutex_init ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1315 (libtsan.so.2+0x5545f) (BuildId: 99ef88596cb10a8ccf307fe1a9070f66a44b1624)
#1 v8::base::RecursiveMutex::RecursiveMutex() <null> (libnode.so.115+0x19c7161) (BuildId: 9cae2928171fe8cd59431ad6df3e33dc66743b19)
Mutex M2 (0x72c00001df30) created at:
#0 pthread_mutex_init ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1315 (libtsan.so.2+0x5545f) (BuildId: 99ef88596cb10a8ccf307fe1a9070f66a44b1624)
#1 v8::internal::Heap::Heap() <null> (libnode.so.115+0x12017cf) (BuildId: 9cae2928171fe8cd59431ad6df3e33dc66743b19)
Thread T1 (tid=24229, running) created by main thread at:
#0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1022 (libtsan.so.2+0x568a6) (BuildId: 99ef88596cb10a8ccf307fe1a9070f66a44b1624)
#1 uv_thread_create_ex <null> (libuv.so.1+0x1ead3) (BuildId: 9ee7ac673eb757eba258b40525cf4de9bbd2de16)
#2 loader_impl_initialize /usr/local/metacall/source/loader/source/loader_impl.c:811 (libmetacalld.so+0x35ed6) (BuildId: 0f692433c24e86392d1358abd90d2cbf294670ca)
#3 loader_impl_load_from_file /usr/local/metacall/source/loader/source/loader_impl.c:1383 (libmetacalld.so+0x363f9) (BuildId: 0f692433c24e86392d1358abd90d2cbf294670ca)
#4 loader_load_from_file /usr/local/metacall/source/loader/source/loader.c:353 (libmetacalld.so+0x32932) (BuildId: 0f692433c24e86392d1358abd90d2cbf294670ca)
#5 metacall_load_from_file_ex /usr/local/metacall/source/metacall/source/metacall.c:505 (libmetacalld.so+0x388dd) (BuildId: 0f692433c24e86392d1358abd90d2cbf294670ca)
#6 metacall_load_from_file /usr/local/metacall/source/metacall/source/metacall.c:480 (libmetacalld.so+0x3892d) (BuildId: 0f692433c24e86392d1358abd90d2cbf294670ca)
#7 metacall_node_callback_test_DefaultConstructor_Test::TestBody() /usr/local/metacall/source/tests/metacall_node_python_callback_test/source/metacall_node_python_callback_test.cpp:51 (metacall-node-python-callback-testd+0x37ed) (BuildId: e8e807ce30b780a73daa6973923972f26145916e)
#8 void testing::internal::HandleSehExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) /usr/local/metacall/build/_deps/googletest-src/googletest/src/gtest.cc:2653 (libgtest.so.1.16.0+0x72f7d) (BuildId: 6877dc67c273608d78762837c6012c5df4e00c62)
#9 void testing::internal::HandleExceptionsInMethodIfSupported<testing::Test, void>(testing::Test*, void (testing::Test::*)(), char const*) /usr/local/metacall/build/_deps/googletest-src/googletest/src/gtest.cc:2689 (libgtest.so.1.16.0+0x72f7d)
#10 testing::Test::Run() /usr/local/metacall/build/_deps/googletest-src/googletest/src/gtest.cc:2728 (libgtest.so.1.16.0+0x4d30b) (BuildId: 6877dc67c273608d78762837c6012c5df4e00c62)
#11 testing::TestInfo::Run() /usr/local/metacall/build/_deps/googletest-src/googletest/src/gtest.cc:2874 (libgtest.so.1.16.0+0x4d755) (BuildId: 6877dc67c273608d78762837c6012c5df4e00c62)
#12 testing::TestSuite::Run() /usr/local/metacall/build/_deps/googletest-src/googletest/src/gtest.cc:3052 (libgtest.so.1.16.0+0x53bc6) (BuildId: 6877dc67c273608d78762837c6012c5df4e00c62)
#13 testing::internal::UnitTestImpl::RunAllTests() /usr/local/metacall/build/_deps/googletest-src/googletest/src/gtest.cc:6004 (libgtest.so.1.16.0+0x6239d) (BuildId: 6877dc67c273608d78762837c6012c5df4e00c62)
#14 bool testing::internal::HandleSehExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /usr/local/metacall/build/_deps/googletest-src/googletest/src/gtest.cc:2653 (libgtest.so.1.16.0+0x73529) (BuildId: 6877dc67c273608d78762837c6012c5df4e00c62)
#15 bool testing::internal::HandleExceptionsInMethodIfSupported<testing::internal::UnitTestImpl, bool>(testing::internal::UnitTestImpl*, bool (testing::internal::UnitTestImpl::*)(), char const*) /usr/local/metacall/build/_deps/googletest-src/googletest/src/gtest.cc:2689 (libgtest.so.1.16.0+0x73529)
#16 testing::UnitTest::Run() /usr/local/metacall/build/_deps/googletest-src/googletest/src/gtest.cc:5583 (libgtest.so.1.16.0+0x4d43e) (BuildId: 6877dc67c273608d78762837c6012c5df4e00c62)
#17 RUN_ALL_TESTS() /usr/local/metacall/build/_deps/googletest-src/googletest/include/gtest/gtest.h:2334 (metacall-node-python-callback-testd+0x3549) (BuildId: e8e807ce30b780a73daa6973923972f26145916e)
#18 main /usr/local/metacall/source/tests/metacall_node_python_callback_test/source/main.cpp:27 (metacall-node-python-callback-testd+0x3549)
Thread T6 (tid=24234, running) created by thread T1 at:
#0 pthread_create ../../../../src/libsanitizer/tsan/tsan_interceptors_posix.cpp:1022 (libtsan.so.2+0x568a6) (BuildId: 99ef88596cb10a8ccf307fe1a9070f66a44b1624)
#1 uv_thread_create_ex <null> (libuv.so.1+0x1ead3) (BuildId: 9ee7ac673eb757eba258b40525cf4de9bbd2de16)
SUMMARY: ThreadSanitizer: data race (/usr/lib/x86_64-linux-gnu/libnode.so.115+0xfd822c) (BuildId: 9cae2928171fe8cd59431ad6df3e33dc66743b19) in v8::platform::DefaultJobState::~DefaultJobState()
Here is the full signal trace:
[pid 24229] rt_sigprocmask(SIG_SETMASK, ~[RTMIN RT_1], [], 8) = 0
[pid 24229] rt_sigaction(SIGWINCH, {sa_handler=SIG_DFL, sa_mask=~[], sa_flags=SA_RESTORER, sa_restorer=0x7f7886ee2df0}, NULL, 8) = 0
[pid 24229] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
[pid 24229] rt_sigprocmask(SIG_BLOCK, [TTOU], NULL, 8) = 0
[pid 24229] rt_sigprocmask(SIG_UNBLOCK, [TTOU], NULL, 8) = 0
[pid 24229] rt_sigprocmask(SIG_BLOCK, [TTOU], NULL, 8) = 0
[pid 24229] rt_sigprocmask(SIG_UNBLOCK, [TTOU], NULL, 8) = 0
[pid 24229] rt_sigprocmask(SIG_BLOCK, [TTOU], NULL, 8) = 0
[pid 24229] rt_sigprocmask(SIG_UNBLOCK, [TTOU], NULL, 8) = 0
[pid 24229] rt_sigaction(SIGHUP, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGINT, NULL, {sa_handler=0x7f7887462be0, sa_mask=~[KILL STOP], sa_flags=SA_RESTORER|SA_RESETHAND|SA_SIGINFO, sa_restorer=0x7f7886ee2df0}, 8) = 0
[pid 24229] rt_sigaction(SIGQUIT, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGILL, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGTRAP, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigprocmask(SIG_BLOCK, ~[], [], 8) = 0
[pid 24229] rt_sigaction(SIGABRT, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
[pid 24229] rt_sigaction(SIGBUS, NULL, {sa_handler=0x7f78874b30d0, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_NODEFER|SA_SIGINFO, sa_restorer=0x7f7886ee2df0}, 8) = 0
[pid 24229] rt_sigaction(SIGFPE, NULL, {sa_handler=0x7f78874b30d0, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_NODEFER|SA_SIGINFO, sa_restorer=0x7f7886ee2df0}, 8) = 0
[pid 24229] rt_sigaction(SIGUSR1, NULL, {sa_handler=0x7f7887462be0, sa_mask=~[KILL STOP], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f7886ee2df0}, 8) = 0
[pid 24229] rt_sigaction(SIGSEGV, NULL, {sa_handler=0x7f7887462be0, sa_mask=~[KILL STOP], sa_flags=SA_RESTORER|SA_SIGINFO, sa_restorer=0x7f7886ee2df0}, 8) = 0
[pid 24229] rt_sigaction(SIGUSR2, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=~[], sa_flags=SA_RESTORER, sa_restorer=0x7f7886ee2df0}, NULL, 8) = 0
[pid 24229] rt_sigaction(SIGALRM, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGTERM, NULL, {sa_handler=0x7f7887462be0, sa_mask=~[KILL STOP], sa_flags=SA_RESTORER|SA_RESETHAND|SA_SIGINFO, sa_restorer=0x7f7886ee2df0}, 8) = 0
[pid 24229] rt_sigaction(SIGSTKFLT, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGCHLD, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGCONT, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGTSTP, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGTTIN, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGTTOU, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGURG, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGXCPU, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGXFSZ, {sa_handler=SIG_IGN, sa_mask=~[], sa_flags=SA_RESTORER, sa_restorer=0x7f7886ee2df0}, NULL, 8) = 0
[pid 24229] rt_sigaction(SIGVTALRM, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGPROF, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGWINCH, NULL, {sa_handler=SIG_DFL, sa_mask=~[KILL STOP], sa_flags=SA_RESTORER, sa_restorer=0x7f7886ee2df0}, 8) = 0
[pid 24229] rt_sigaction(SIGIO, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGPWR, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24229] rt_sigaction(SIGSYS, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
[pid 24234] rt_sigprocmask(SIG_BLOCK, ~[RT_1], NULL, 8) = 0
[pid 24234] exit(0) = ?
[pid 24234] +++ exited with 0 +++
[pid 24231] rt_sigprocmask(SIG_BLOCK, ~[RT_1], <unfinished ...>
[pid 24230] rt_sigprocmask(SIG_BLOCK, ~[RT_1], <unfinished ...>
[pid 24231] <... rt_sigprocmask resumed>NULL, 8) = 0
[pid 24230] <... rt_sigprocmask resumed>NULL, 8) = 0
[pid 24230] exit(0 <unfinished ...>
[pid 24231] exit(0 <unfinished ...>
[pid 24230] <... exit resumed>) = ?
[pid 24230] +++ exited with 0 +++
[pid 24231] <... exit resumed>) = ?
[pid 24231] +++ exited with 0 +++
[pid 24233] rt_sigprocmask(SIG_BLOCK, ~[RT_1], NULL, 8) = 0
[pid 24233] exit(0) = ?
[pid 24233] +++ exited with 0 +++
[pid 24232] rt_sigprocmask(SIG_BLOCK, ~[RT_1], NULL, 8) = 0
[pid 24232] exit(0) = ?
[pid 24232] +++ exited with 0 +++
THREAD EXIT after node::Start
[pid 24229] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0xd0} ---
ThreadSanitizer: CHECK failed: tsan_interceptors_posix.cpp:2058 "((thr->slot)) != (0)" (0x0, 0x0) (tid=24229)
[pid 24229] --- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0x14f88} ---
[pid 24235] +++ killed by SIGSEGV (core dumped) +++
[pid 24228] +++ killed by SIGSEGV (core dumped) +++
[pid 24229] +++ killed by SIGSEGV (core dumped) +++
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
The issue happens here:
https://github.com/metacall/core/blob/67687c603b343d4fb5fb73b28601a594569a1e6d/source/loaders/node_loader/source/node_loader_impl.cpp#L4906
After it exits from the thread.
The following tests are involved, all of them share the same nature, they launch node from python. I have disabled the python async support and the issue still persist so it's unrelated to that:
#15 789.1 49 - metacall-node-python-port-mock-test (SEGFAULT) metacall-node-python-port-mock-test
#15 789.1 50 - metacall-node-python-port-ruby-test (SEGFAULT) metacall-node-python-port-ruby-test
#15 789.1 52 - metacall-node-callback-test (SEGFAULT) metacall-node-callback-test
#15 789.1 57 - metacall-node-python-async-after-destroy-test (SEGFAULT) metacall-node-python-async-after-destroy-test
#15 789.1 58 - metacall-node-python-await-test (SEGFAULT) metacall-node-python-await-test
#15 789.1 59 - metacall-node-python-exception-test (SEGFAULT) metacall-node-python-exception-test
All of them show the same assert error:
ThreadSanitizer: CHECK failed: tsan_interceptors_posix.cpp:2058 "((thr->slot)) != (0)" (0x0, 0x0) (tid=9917)
I am not sure if this an issue of nodejs itself or it is related to metacall node_loader and how the destruction mechanism is handled.
- Dominant language
- C
- Stars
- 1.8k
- Forks
- 266
- Avg merge
- 2d 16h
- Merged PRs (30d)
- 14
Getting set up
- Ships a Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from metacall/core
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
metacall/core#964 · 1 comment ·
Maintainers usually reply within 1 day
-
pre-push hook empties VERSION when the clone has no tagsPossibly taken @vedant21-oss claimed this 18 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
metacall/core#939 · 3 comments ·
Maintainers usually reply within 1 day
-
TOCTOU race condition vulnerability!Possibly taken @akshatchitransh claimed this 1 day ago. Openbug
Difficulty 4/5 3-5 days Newbie friendliness 35/100
metacall/core#961 · 2 comments ·
Maintainers usually reply within 1 day
-
bug
Difficulty 3/5 Half a day Newbie friendliness 70/100
Maintainers usually reply within 1 day
-
Go Port Race Conditions on MacOSPossibly taken A pull request linked to this issue is open or already merged. Openbug
Difficulty 4/5 3-5 days Newbie friendliness 35/100
metacall/core#942 · 3 comments ·
Maintainers usually reply within 1 day
Similar issues
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
-
`table_create()` crashes when an `hccalloc()` for the option table failsPossibly taken @marinelay claimed this today. Opentype: bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 78/100
Maintainers usually reply within 1 day
-
bug severity: low
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
AlessandroSangiuliano/Uros#950 ·
-
vim.text.indent(): expandtab fails on single-tab blank lines and CRLF blank lines reset old_indent to 0Possibly taken A pull request linked to this issue is open or already merged. Openlua
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 1 day