diff --git a/Makefile b/Makefile
index 7c5043867..fbe092472 100644
--- a/Makefile
+++ b/Makefile
@@ -79,7 +79,8 @@ TEST_PREREQ += lib/libtestutil-closedir$(SHLIB_SUFFIX) \
lib/libtestutil-0getgroups$(SHLIB_SUFFIX) \
lib/libtestutil-dupgetgroups$(SHLIB_SUFFIX) \
lib/libtestutil-time$(SHLIB_SUFFIX) \
- lib/libtestutil-mktime$(SHLIB_SUFFIX)
+ lib/libtestutil-mktime$(SHLIB_SUFFIX) \
+ lib/libtestutil-tcl_loadfile$(SHLIB_SUFFIX)
endif
endif
@@ -660,6 +661,9 @@ lib/libtestutil-time$(SHLIB_SUFFIX):
lib/libtestutil-mktime$(SHLIB_SUFFIX):
$(MAKE) --no-print-directory -C lib $(@F)
+lib/libtestutil-tcl_loadfile$(SHLIB_SUFFIX):
+ $(MAKE) --no-print-directory -C lib $(@F)
+
# example configs for test rules
testsuite/example/.modulespath: testsuite/example/.modulespath.in
$(translate-in-script)
@@ -1172,6 +1176,7 @@ $(V).SILENT: initdir pkgdoc doc version.inc share/rpm/environment-modules.spec \
lib/libtestutil-getpwuid$(SHLIB_SUFFIX) lib/libtestutil-getgroups$(SHLIB_SUFFIX) \
lib/libtestutil-0getgroups$(SHLIB_SUFFIX) lib/libtestutil-mktime$(SHLIB_SUFFIX) \
lib/libtestutil-dupgetgroups$(SHLIB_SUFFIX) lib/libtestutil-time$(SHLIB_SUFFIX) \
+ lib/libtestutil-tcl_loadfile$(SHLIB_SUFFIX) \
testsuite/example/.modulespath testsuite/example/modulespath-wild \
testsuite/example/modulerc testsuite/example/initrc-1 testsuite/example/initrc \
dist-tar dist-gzip dist-bzip2 dist-win $(MODULECMDTEST) $(MODULECMDTEST)_i
diff --git a/NEWS.rst b/NEWS.rst
index f81d73123..06ab3049d 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -130,6 +130,8 @@ Modules 5.7.0 (not yet released)
or :subcmd:`sh-to-mod` sub-command, does not get access to shell aliases,
functions or completions settings only available in interactive sessions or
defined in profile files. (fix issue #646)
+* Report a global known error when Modules Tcl extension library cannot be
+ loaded. (fix issue #644)
.. _5.6 release notes:
diff --git a/lib/.gitignore b/lib/.gitignore
index b52804364..f968b7127 100644
--- a/lib/.gitignore
+++ b/lib/.gitignore
@@ -16,6 +16,7 @@
/libtestutil-dupgetgroups.so
/libtestutil-time.so
/libtestutil-mktime.so
+/libtestutil-tcl_loadfile.so
/envmodules.c.gcov
/envmodules.gcda
/envmodules.gcno
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 68aefbf43..4393d61d8 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -60,6 +60,10 @@ libtestutil-mktime@SHLIB_SUFFIX@: testutil-mktime.c
$(ECHO_CC)
$(LDCC) $< -o $@
+libtestutil-tcl_loadfile@SHLIB_SUFFIX@: testutil-tcl_loadfile.c
+ $(ECHO_CC)
+ $(LDCC) $< -o $@
+
clean:
rm -f envmodules.o
rm -f libtclenvmodules@TCL_SHLIB_SUFFIX@
@@ -70,6 +74,7 @@ clean:
rm -f libtestutil-dupgetgroups@SHLIB_SUFFIX@
rm -f libtestutil-time@SHLIB_SUFFIX@
rm -f libtestutil-mktime@SHLIB_SUFFIX@
+ rm -f libtestutil-tcl_loadfile@SHLIB_SUFFIX@
rm -f envmodules.c.gcov envmodules.gcda envmodules.gcno
distclean: clean
@@ -95,4 +100,4 @@ $(V).SILENT: envmodules.o libtclenvmodules@TCL_SHLIB_SUFFIX@ \
libtestutil-closedir@SHLIB_SUFFIX@ libtestutil-getpwuid@SHLIB_SUFFIX@ \
libtestutil-getgroups@SHLIB_SUFFIX@ libtestutil-0getgroups@SHLIB_SUFFIX@ \
libtestutil-dupgetgroups@SHLIB_SUFFIX@ libtestutil-time@SHLIB_SUFFIX@ \
- libtestutil-mktime@SHLIB_SUFFIX@
+ libtestutil-mktime@SHLIB_SUFFIX@ libtestutil-tcl_loadfile@SHLIB_SUFFIX@
diff --git a/lib/testutil-tcl_loadfile.c b/lib/testutil-tcl_loadfile.c
new file mode 100644
index 000000000..57527eb04
--- /dev/null
+++ b/lib/testutil-tcl_loadfile.c
@@ -0,0 +1,35 @@
+/*************************************************************************
+ *
+ * TESTUTIL-TCL_LOADFILE.C, Superseded Tcl_LoadFile function for test purpose
+ * Copyright (C) 2026 Xavier Delaruelle
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ ************************************************************************/
+
+#include
+
+#if !defined (__APPLE__)
+
+int Tcl_LoadFile(Tcl_Interp *interp, Tcl_Obj *pathPtr,
+ const char *const symbols[], int flags, void *procVPtrs,
+ Tcl_LoadHandle *handlePtr)
+{
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf("error message"));
+ return TCL_ERROR;
+}
+
+#endif
+
+/* vim:set tabstop=3 shiftwidth=3 expandtab autoindent: */
diff --git a/tcl/main.tcl.in b/tcl/main.tcl.in
index 59ad2de6d..e16452542 100644
--- a/tcl/main.tcl.in
+++ b/tcl/main.tcl.in
@@ -625,7 +625,11 @@ if {[catch {
##nagelfar ignore +2 Too long line
@libtclenvmodules@if {[file readable [getConf tcl_ext_lib]]} {
@libtclenvmodules@ reportDebug "Load Tcl extension library ([getConf tcl_ext_lib])"
- @libtclenvmodules@ load [file normalize [getConf tcl_ext_lib]] Envmodules
+ @libtclenvmodules@ if {[catch {
+ @libtclenvmodules@ load [file normalize [getConf tcl_ext_lib]] Envmodules
+ @libtclenvmodules@ } errMsg]} {
+ @libtclenvmodules@ reportErrorAndExit $errMsg
+ @libtclenvmodules@ }
@libtclenvmodules@ setState tcl_ext_lib_loaded 1
@libtclenvmodules@}
# use fallback procs if extension library is not loaded
diff --git a/testsuite/modules.00-init/005-init_ts.exp b/testsuite/modules.00-init/005-init_ts.exp
index 705d2e601..7ac00fb45 100644
--- a/testsuite/modules.00-init/005-init_ts.exp
+++ b/testsuite/modules.00-init/005-init_ts.exp
@@ -168,6 +168,7 @@ if {$install_libtclenvmodules eq {y}} {
set dupgetgroupslib_file lib/libtestutil-dupgetgroups$install_shlib_suffix
set timelib_file lib/libtestutil-time$install_shlib_suffix
set mktimelib_file lib/libtestutil-mktime$install_shlib_suffix
+ set tcl_loadfilelib_file lib/libtestutil-tcl_loadfile$install_shlib_suffix
}
# check file permission capabilities
diff --git a/testsuite/modules.00-init/150-access-init.exp b/testsuite/modules.00-init/150-access-init.exp
index 09297561d..3ae31096d 100644
--- a/testsuite/modules.00-init/150-access-init.exp
+++ b/testsuite/modules.00-init/150-access-init.exp
@@ -75,7 +75,7 @@ if {[info exists tclextlib_file]} {
} else {
set tserr "$error_msgs: couldn't load file \"$tclextlib_file_abs_re\":.*"
}
- testouterr_cmd_re sh {-V} {} $tserr
+ testouterr_cmd_re sh {-V} ERR $tserr
file delete $tclextlib_file
file rename $tclextlib_file.orig $tclextlib_file
}
diff --git a/testsuite/modules.00-init/190-tcl_ext_lib.exp b/testsuite/modules.00-init/190-tcl_ext_lib.exp
new file mode 100644
index 000000000..56a097e63
--- /dev/null
+++ b/testsuite/modules.00-init/190-tcl_ext_lib.exp
@@ -0,0 +1,44 @@
+##############################################################################
+# Modules Revision 3.0
+# Providing a flexible user environment
+#
+# File: modules.00-init/%M%
+# Revision: %I%
+# First Edition: 2026/07/17
+# Last Mod.: %U%, %G%
+#
+# Authors: Xavier Delaruelle, xavier.delaruelle@cea.fr
+#
+# Description: Testuite testsequence
+# Command: load
+# Modulefiles: bar, foo
+# Sub-Command:
+#
+# Comment: %C{
+# Test Modules Tcl extended library
+# }C%
+#
+##############################################################################
+
+skip_if_quick_mode
+
+# test error report when loading library
+# ensure we are in a specific test mode so we can play with installed files
+# no need to specifically test on Darwin system and on Tcl < 9
+if {[siteconfig_isStderrTty] && $tcl_platform(os) ne "Darwin" &&\
+ [info exists tclextlib_file] && [info exists tcl_loadfilelib_file] &&\
+ [file exists $tcl_loadfilelib_file] && [cmpversion $tclsh_version 9.0] > -1} {
+ setenv_var LD_PRELOAD $tcl_loadfilelib_file
+
+ set tserr "$error_msgs: error message"
+ testouterr_cmd sh {-V} ERR $tserr
+
+ unsetenv_var LD_PRELOAD
+}
+
+
+#
+# Cleanup
+#
+
+reset_test_env