Introduction
Packaging Python applications with PyInstaller is usually a smooth process—until you introduce IBM DB2 connectivity using the ibm_db package. Suddenly, what should be a simple build turns into a maze of cryptic errors and missing dependencies. If you’ve ever spent hours (or even days) trying to figure out why your executable won’t run, you’re not alone.
Note: This guide focuses on resolving these issues in a Linux environment.
In this guide, we’ll walk you through a real-world issue we faced and share the step-by-step solution that finally worked. By the end, you’ll have a clear roadmap to package your DB2-enabled Python app without headaches.
The Problem: SQL10007N Error
When we packaged our Python application using PyInstaller, everything seemed to work fine during development. However, when we ran the bundled executable, we encountered this error:
Exception: [IBM][CLI Driver] SQL10007N Message "0" could not be retrieved. Reason code: "3". SQLSTATE=42724 SQLCODE=-10007
This error is particularly frustrating because:
- The error message itself is cryptic (a message about not being able to retrieve a message!)
- It works perfectly in development but fails in the PyInstaller bundle
- The reason code "3" doesn't provide obvious clues
- Standard PyInstaller configurations don't catch the issue
Understanding the Root Causes
After extensive debugging, we identified four critical issues that cause this problem:
1. Missing ICC Libraries
The IBM DB2 clidriver includes IBM Common Client (ICC) libraries in the clidriver/lib/icc/ directory. These libraries are essential for DB2 connectivity but PyInstaller doesn't automatically detect and bundle them.
Why it matters: Without these libraries, DB2 cannot properly initialize its client components, leading to the SQL10007N error.
2. Locale Mismatch
The DB2 clidriver ships with locale files in a specific format (e.g., en_US.iso88591), but modern Linux systems expect UTF-8 locales (e.g., en_US.UTF-8). When the expected locale directory doesn't exist, DB2 fails to load error messages.
Why it matters: This is the direct cause of the "Message '0' could not be retrieved" error - DB2 literally cannot find the message files.
3. Conflicting libdb2.so.1
PyInstaller's automatic dependency detection sometimes bundles a standalone libdb2.so.1 file that has undefined symbols and conflicts with the complete DB2 client libraries.
Why it matters: This causes symbol resolution errors and prevents proper DB2 initialization.
4. Incomplete Library Path Configuration
Even when libraries are bundled, they need to be in the correct search path, with the ICC directory having the highest priority.
Why it matters: Without proper path configuration, the system may load incorrect or incomplete libraries.
Prerequisites
Before starting, ensure you have:
- Python 3.8 or higher installed
- Access to a DB2 database (for connection testing)
- Basic knowledge of Python and command line
1. Create Project Structure
Create your project directory and basic structure:
mkdir my_db2_app
cd my_db2_app
mkdir -p src/my_app
touch src/my_app/app.py
touch runtime_hook_db2.py
touch my_app.spec
touch build.py
touch requirements.txt
Your project structure should look like:
my_db2_app/
├── src/
│ └── my_app/
│ └── app.py # Your main application
├── runtime_hook_db2.py # DB2 runtime hook (from template)
├── my_app.spec # PyInstaller spec file (from template)
├── build.py # Build script (from template)
└── requirements.txt # Python dependencies
2. Create Virtual Environment
On Linux/macOS:
python3 -m venv venv
source venv/bin/activate
which python
On Windows:
# Create virtual environment
python -m venv venv
# Activate virtual environment
venv\Scripts\activate
# Verify activation
where python
3. Install Dependencies
Create requirements.txt with these dependencies:
# IBM DB2 driver
ibm_db>=3.2.0
# PyInstaller for packaging
pyinstaller>=6.0.0
Install all dependencies:
pip install --upgrade pip
pip install -r requirements.txt
python -c "import ibm_db; print(f'ibm_db version: {ibm_db.__version__}')"
Expected output:
ibm_db version: 3.2.7
Step 4: Create the Application File
Now let's create the main application that will test DB2 connectivity.
File: src/my_app/app.py
"""
DB2 Application with Connection Test
This example shows how to connect to DB2 and run queries
"""
import sys
import os
def print_environment():
"""Print DB2-related environment variables for debugging"""
print("\n" + "="*60)
print("Environment Variables:")
print("="*60)
env_vars = ['DB2_HOME', 'IBM_DB_HOME', 'DB2CODEPAGE', 'LD_LIBRARY_PATH']
for var in env_vars:
value = os.environ.get(var, 'NOT SET')
if len(value) > 80:
value = value[:80] + '...'
print(f"{var}: {value}")
print("="*60 + "\n")
def test_import():
"""Test if ibm_db can be imported"""
print("Testing ibm_db import...")
try:
import ibm_db
version = ibm_db.__version__ if hasattr(ibm_db, '__version__') else 'Unknown'
print(f"✓ ibm_db imported successfully (version: {version})")
return True
except Exception as e:
print(f"✗ Failed to import ibm_db: {e}")
return False
def test_connection(conn_string):
"""Test DB2 connection and run sample queries"""
print(f"\nTesting DB2 connection...")
try:
import ibm_db
print(f"Connecting to database...")
conn = ibm_db.connect(conn_string, "", "")
print("✓ Connected to DB2 successfully!")
print("\n--- Test 1: Current Timestamp ---")
sql = "SELECT CURRENT TIMESTAMP FROM SYSIBM.SYSDUMMY1"
stmt = ibm_db.exec_immediate(conn, sql)
result = ibm_db.fetch_tuple(stmt)
if result:
print(f"✓ Current timestamp: {result[0]}")
print("\n--- Test 2: Database Version ---")
sql = "SELECT SERVICE_LEVEL, FIXPACK_NUM FROM SYSIBMADM.ENV_INST_INFO"
stmt = ibm_db.exec_immediate(conn, sql)
result = ibm_db.fetch_tuple(stmt)
if result:
print(f"✓ DB2 Version: {result[0]}, Fixpack: {result[1]}")
print("\n--- Test 3: Sample Tables ---")
sql = """
SELECT TABSCHEMA, TABNAME, TYPE
FROM SYSCAT.TABLES
WHERE TABSCHEMA NOT LIKE 'SYS%'
FETCH FIRST 5 ROWS ONLY
"""
stmt = ibm_db.exec_immediate(conn, sql)
count = 0
while True:
result = ibm_db.fetch_tuple(stmt)
if not result:
break
print(f" {result[0]}.{result[1]} ({result[2]})")
count += 1
print(f"✓ Found {count} tables")
ibm_db.close(conn)
print("\n✓ Connection closed successfully")
return True
except Exception as e:
print(f"✗ Connection/Query failed: {e}")
import traceback
traceback.print_exc()
return False
def main():
"""Main application entry point"""
print("\n" + "="*60)
print("DB2 Application - Connection Test")
print("="*60)
if getattr(sys, 'frozen', False):
print("✓ Running in PyInstaller bundle")
print(f"✓ Bundle directory: {sys._MEIPASS}")
else:
print("⚠ Running in normal Python environment")
print_environment()
if not test_import():
print("\n✗ Import test failed. Exiting.")
return 1
conn_string = os.environ.get('DB2_CONN_STRING')
if not conn_string and len(sys.argv) > 1:
conn_string = sys.argv[1]
if not conn_string:
print("\n" + "="*60)
print("Connection Test Skipped")
print("="*60)
print("To test DB2 connection, provide connection string:")
print("\nOption 1 - Environment variable:")
print(" export DB2_CONN_STRING='DATABASE=SAMPLE;HOSTNAME=localhost;PORT=50000;UID=db2inst1;PWD=password'")
print(" python src/my_app/app.py")
print("\nOption 2 - Command line argument:")
print(" python src/my_app/app.py 'DATABASE=SAMPLE;HOSTNAME=localhost;PORT=50000;UID=db2inst1;PWD=password'")
print("\nConnection string format:")
print(" DATABASE=<dbname>;HOSTNAME=<host>;PORT=<port>;UID=<user>;PWD=<password>")
print("="*60)
print("\n✓ Import test passed - ibm_db is working!")
return 0
if test_connection(conn_string):
print("\n" + "="*60)
print("✓ ALL TESTS PASSED!")
print("="*60)
return 0
else:
print("\n" + "="*60)
print("✗ CONNECTION TEST FAILED")
print("="*60)
return 1
if __name__ == "__main__":
sys.exit(main())
What this file does: This is your main application file that demonstrates DB2 connectivity. It includes three main functions:
test_import() - Verifies that the ibm_db module can be imported successfully
test_connection() - Connects to a DB2 database and runs sample queries
print_environment() - Displays DB2-related environment variables for debugging
The application can run in two modes: with or without a database connection. If no connection string is provided, it will only test the import functionality, which is useful for verifying that PyInstaller has bundled everything correctly.
Customizable parts:
- You can modify the SQL queries in
test_connection() to match your specific needs
- Add additional test functions for your application logic
- Customize the connection string format if needed
Step 5: Create the Runtime Hook
The runtime hook is crucial—it runs before your application starts and sets up the DB2 environment correctly.
File: runtime_hook_db2.py
import os
import sys
if getattr(sys, 'frozen', False):
bundle_dir = sys._MEIPASS
clidriver_path = os.path.join(bundle_dir, 'clidriver')
if os.path.exists(clidriver_path):
print(f"✓ Using bundled CLI driver: {clidriver_path}")
os.environ['DB2_HOME'] = clidriver_path
os.environ['IBM_DB_HOME'] = clidriver_path
os.environ['DB2CODEPAGE'] = '1208'
msg_path = os.path.join(clidriver_path, 'msg')
if os.path.exists(msg_path):
os.environ['DB2_MSG_PATH'] = msg_path
os.environ['DB2_CLI_MSG_PATH'] = msg_path
try:
import shutil
iso88591_path = os.path.join(msg_path, 'en_US.iso88591')
en_us_path = os.path.join(msg_path, 'en_US')
en_us_utf8_path = os.path.join(msg_path, 'en_US.UTF-8')
if os.path.exists(iso88591_path) and not os.path.exists(en_us_path):
os.rename(iso88591_path, en_us_path)
print(f"✓ Renamed message catalog: en_US.iso88591 -> en_US")
if os.path.exists(en_us_path) and not os.path.exists(en_us_utf8_path):
shutil.copytree(en_us_path, en_us_utf8_path)
print(f"✓ Copied message catalog: en_US -> en_US.UTF-8")
except Exception as e:
print(f"⚠ Warning: Could not create locale directories: {e}")
lib_path = os.path.join(clidriver_path, 'lib')
icc_path = os.path.join(lib_path, 'icc')
lib_paths = []
if os.path.exists(icc_path):
lib_paths.append(icc_path)
if os.path.exists(lib_path):
lib_paths.append(lib_path)
lib_paths.append(bundle_dir)
if lib_paths:
lib_path_str = os.pathsep.join(lib_paths)
if sys.platform == 'darwin':
os.environ['DYLD_LIBRARY_PATH'] = lib_path_str + os.pathsep + os.environ.get('DYLD_LIBRARY_PATH', '')
elif sys.platform.startswith('linux'):
os.environ['LD_LIBRARY_PATH'] = lib_path_str + os.pathsep + os.environ.get('LD_LIBRARY_PATH', '')
elif sys.platform == 'win32':
os.environ['PATH'] = lib_path_str + os.pathsep + os.environ.get('PATH', '')
print(f"✓ Library paths configured")
print(f"✓ DB2 environment ready")
What this file does: The runtime hook is executed by PyInstaller before your application starts. It performs several critical tasks:
- Detects PyInstaller environment - Checks if the app is running in a bundled executable
- Sets DB2 environment variables - Configures
DB2_HOME, IBM_DB_HOME, and DB2CODEPAGE to point to the bundled clidriver
- Fixes locale mismatch - Creates the
en_US.UTF-8 locale directory that Linux systems expect (this solves the SQL10007N error)
- Configures library paths - Sets
LD_LIBRARY_PATH to include both the main lib directory and the ICC subdirectory, with ICC having priority
This file addresses all four root causes we identified earlier. The locale fix is particularly important—without it, DB2 cannot load error messages, resulting in the cryptic SQL10007N error.
Customizable parts:
- If you need to support additional locales, add them in the locale fix section
- For Windows deployment, you may need to adjust the library path configuration
Step 6: Create the PyInstaller Spec File
The spec file tells PyInstaller exactly what to bundle and how to build your executable.
File: my_app.spec
import os
import importlib.util
def get_package_path(package_name):
"""Find where ibm_db is installed"""
spec = importlib.util.find_spec(package_name)
if spec and spec.submodule_search_locations:
return spec.submodule_search_locations[0]
elif spec and spec.origin:
return os.path.dirname(spec.origin)
return None
ibm_db_path = get_package_path('ibm_db')
clidriver_path = os.path.join(ibm_db_path, 'clidriver') if ibm_db_path else None
a = Analysis(
['src/my_app/app.py'],
pathex=[],
binaries=[],
datas=[(clidriver_path, 'clidriver')] if clidriver_path and os.path.exists(clidriver_path) else [],
hiddenimports=['ibm_db', 'ibm_db_dbi'],
hookspath=[],
hooksconfig={},
runtime_hooks=['runtime_hook_db2.py'],
excludes=[],
noarchive=False,
optimize=0,
)
filtered_binaries = []
for name, path, typecode in a.binaries:
if ('libdb2.' in name or 'libdb2.so' in name) and 'clidriver' not in path:
print(f"Excluding conflicting library: {name} from {path}")
continue
filtered_binaries.append((name, path, typecode))
a.binaries = filtered_binaries
if clidriver_path and os.path.exists(clidriver_path):
lib_path = os.path.join(clidriver_path, 'lib')
if os.path.exists(lib_path):
import glob
for lib_file in glob.glob(os.path.join(lib_path, '*.so*')):
lib_name = os.path.basename(lib_file)
if not any(name == lib_name for name, _, _ in a.binaries):
a.binaries.append((lib_name, lib_file, 'BINARY'))
print(f"Adding clidriver library: {lib_name}")
icc_path = os.path.join(lib_path, 'icc')
if os.path.exists(icc_path):
for lib_file in glob.glob(os.path.join(icc_path, '*.so*')):
lib_name = os.path.basename(lib_file)
if not any(name == lib_name for name, _, _ in a.binaries):
a.binaries.append((lib_name, lib_file, 'BINARY'))
print(f"Adding clidriver/icc library: {lib_name}")
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name='my_app',
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=True,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
)
What this file does: The spec file is PyInstaller's configuration file that controls the build process. It performs several key functions:
- Locates the clidriver - Uses
importlib to find where ibm_db is installed and locates its bundled clidriver
- Bundles the clidriver - Adds the entire clidriver directory (including message files and configs) to the executable
- Filters conflicting libraries - Removes standalone
libdb2.so.1 files that PyInstaller might find, which would conflict with the complete clidriver libraries
- Explicitly adds all necessary libraries - Ensures both the main lib directory and the ICC subdirectory libraries are included
- Configures the runtime hook - Tells PyInstaller to run
runtime_hook_db2.py before the application starts
The three "CRITICAL FIX" sections address the library-related root causes we identified. Without these fixes, the executable would either fail to find libraries or load conflicting versions.
Customizable parts:
- Line 19: Change
'src/my_app/app.py' to point to your main Python file
- Line 21: Add any additional hidden imports your application needs (e.g.,
'sqlalchemy', 'fastapi')
- Line 69: Change
name='my_app' to your desired executable name
- Line 77: Set
console=False if you're building a GUI application
Step 7: Create the Build Script
The build script automates the PyInstaller build process.
File: build.py
import os
import sys
import subprocess
def main():
print("="*60)
print("Building with PyInstaller")
print("="*60)
spec_file = 'my_app.spec'
if not os.path.exists(spec_file):
print(f"✗ Spec file not found: {spec_file}")
sys.exit(1)
cmd = ['pyinstaller', '--clean', '--noconfirm', spec_file]
print(f"\nRunning: {' '.join(cmd)}\n")
print("="*60)
result = subprocess.run(cmd)
if result.returncode != 0:
print("\n" + "="*60)
print("✗ Build failed!")
print("="*60)
sys.exit(1)
print("\n" + "="*60)
print("✓ Build completed successfully!")
print("="*60)
print(f"\nExecutable: dist/my_app")
print("\nTo test:")
print(" ./dist/my_app")
print("="*60)
if __name__ == "__main__":
main()
What this file does: This is a simple Python script that automates the build process. It:
- Checks for the spec file - Verifies that
my_app.spec exists before attempting to build
- Runs PyInstaller - Executes PyInstaller with the
--clean flag (removes previous build artifacts) and --noconfirm flag (overwrites without prompting)
- Reports the result - Displays success or failure messages and shows where the executable was created
Using this script is more convenient than typing the PyInstaller command manually, and it ensures consistent build parameters.
Customizable parts:
- Line 11: Change
'my_app.spec' if you renamed your spec file
- Line 32: Update the executable path if you changed the app name in the spec file
How to Build and Test
Now that all files are in place, let's build and test the application.
Build the Application
python build.py
What happens during the build:
- PyInstaller analyzes your application and its dependencies
- It bundles the clidriver directory (including all libraries and message files)
- It filters out conflicting libraries
- It explicitly adds ICC libraries
- It creates a standalone executable in the
dist/my_app/ directory
Expected output:
============================================================
Building with PyInstaller
============================================================
Running: pyinstaller --clean --noconfirm my_app.spec
============================================================
... (PyInstaller output) ...
Excluding conflicting library: libdb2.so.1 from /usr/lib/...
Adding clidriver library: libdb2.so.1
Adding clidriver library: libdb2o.so.1
Adding clidriver/icc library: libibmc++.so.1
Adding clidriver/icc library: libibmdb2.so.1
... (more libraries) ...
============================================================
✓ Build completed successfully!
============================================================
Executable: dist/my_app
To test:
./dist/my_app
============================================================
Test the Application
Test 1: Import Test (No Database Required)
This test verifies that PyInstaller bundled everything correctly:
./dist/my_app
Expected output:
✓ Using bundled CLI driver: /tmp/_MEIRhQQgx/clidriver
✓ Renamed message catalog: en_US.iso88591 -> en_US
✓ Copied message catalog: en_US -> en_US.UTF-8
✓ Library paths configured
✓ DB2 environment ready
============================================================
DB2 Application - Connection Test
============================================================
✓ Running in PyInstaller bundle
✓ Bundle directory: /tmp/_MEIRhQQgx
============================================================
Environment Variables:
============================================================
DB2_HOME: /tmp/_MEIRhQQgx/clidriver
IBM_DB_HOME: /tmp/_MEIRhQQgx/clidriver
DB2CODEPAGE: 1208
LD_LIBRARY_PATH: /tmp/_MEIRhQQgx/clidriver/lib/icc:/tmp/_MEIRhQQgx/clidriver/lib:/tmp/_MEIRhQQgx:...
============================================================
Testing ibm_db import...
✓ ibm_db imported successfully (version: 3.2.7)
============================================================
Connection Test Skipped
============================================================
To test DB2 connection, provide connection string:
Option 1 - Environment variable:
export DB2_CONN_STRING='DATABASE=SAMPLE;HOSTNAME=localhost;PORT=50000;UID=db2inst1;PWD=password'
python src/my_app/app.py
Option 2 - Command line argument:
python src/my_app/app.py 'DATABASE=SAMPLE;HOSTNAME=localhost;PORT=50000;UID=db2inst1;PWD=password'
Connection string format:
DATABASE=<dbname>;HOSTNAME=<host>;PORT=<port>;UID=<user>;PWD=<password>
============================================================
✓ Import test passed - ibm_db is working!
If you see this output, PyInstaller bundling is working correctly!
Test 2: Connection Test (Requires DB2 Database)
To test with an actual DB2 database, provide a connection string:
Option 1: Using environment variable
export DB2_CONN_STRING='DATABASE=SAMPLE;HOSTNAME=localhost;PORT=50000;UID=db2inst1;PWD=password'
./dist/my_app
Option 2: Using command line argument
./dist/my_app/my_app 'DATABASE=SAMPLE;HOSTNAME=localhost;PORT=50000;UID=db2inst1;PWD=password'
Expected output:
... (environment setup output) ...
Testing DB2 connection...
Connecting to database...
✓ Connected to DB2 successfully!
--- Test 1: Current Timestamp ---
✓ Current timestamp: 2025-12-05 16:00:00.000000
--- Test 2: Database Version ---
✓ DB2 Version: DB2 v11.5.9.0, Fixpack: 0
--- Test 3: Sample Tables ---
MYSCHEMA.CUSTOMERS (T)
MYSCHEMA.ORDERS (T)
MYSCHEMA.PRODUCTS (T)
✓ Found 3 tables
✓ Connection closed successfully
============================================================
✓ ALL TESTS PASSED!
============================================================
Understanding PyInstaller's Temporary Directory
How PyInstaller Works
When you run a PyInstaller executable, it:
- Extracts to a temporary directory - Creates
/tmp/_MEIxxxxxx/ (Linux) or C:\Users\...\AppData\Local\Temp\_MEIxxxxxx\ (Windows)
- Unpacks all bundled files - Extracts the clidriver, libraries, and your application code
- Runs your application - Executes from this temporary location
- Cleans up on exit - Deletes the temporary directory when the app closes
What's in the dist/ Directory?
The dist/ directory contains a single executable file:
dist/
└── my_app # Single executable (71MB) - everything bundled inside
Note: We're using PyInstaller's onefile mode, which packages everything into a single executable. All files (clidriver, libraries, your code) are embedded inside this one binary file.
What Happens at Runtime?
1. User runs: ./dist/my_app
2. PyInstaller creates: /tmp/_MEI6wsSpq/
3. Extracts clidriver to: /tmp/_MEI6wsSpq/clidriver/
4. Runtime hook runs and sets: DB2_HOME=/tmp/_MEI6wsSpq/clidriver
5. Your app runs with DB2 pointing to the temp location
6. On exit: /tmp/_MEI6wsSpq/ is deleted
Important: The /tmp/_MEI... path changes every time you run the app. This is normal PyInstaller behavior.
Troubleshooting
1. Problem: Import test fails with "undefined symbol: sqloRemStgDelete"
Cause: Conflicting libdb2 libraries
What's happening: PyInstaller found a standalone libdb2.so.1 file (usually from system libraries) that has incomplete symbols. This conflicts with the complete clidriver libraries.
Solution:
python build.py 2>&1 | grep "Excluding conflicting"
grep -A 10 "CRITICAL FIX #1" my_app.spec
find dist/my_app/_internal -name "libdb2*"
If still failing: The filtering logic in my_app.spec may need adjustment for your system's library paths.
2. Problem: Import test fails with "SQL10007N Message '0' could not be retrieved"
Cause: Missing locale directories or ICC libraries
What's happening: DB2 is trying to load error messages but can't find the locale files, or the ICC libraries aren't bundled.
Solution:
./dist/my_app/my_app &
APP_PID=$!
ls -la /tmp/_MEI*/clidriver/msg/
ls -la /tmp/_MEI*/clidriver/lib/icc/
kill $APP_PID
./dist/my_app/my_app 2>&1 | grep "✓"
If locale not created: Check that runtime_hook_db2.py is in your project root and referenced in the spec file.
If ICC libraries missing: Check the spec file has the ICC bundling code (CRITICAL FIX #3).
3. Problem: Connection test fails but import test passes
Cause: This is NOT a packaging issue—it's environment/network related
What's happening: PyInstaller bundling is working correctly (import succeeded), but the database connection is failing due to network, credentials, or database configuration issues.
Common error codes:
- SQL30081N - Communication link failure (network issue)
- SQL1336N - Invalid credentials
- SQL1013N - Database not found
- SQL30082N - Connection timeout
Debug steps:
telnet your_hostname your_port
nc -zv your_hostname your_port
python -c 'import ibm_db; conn = ibm_db.connect("DATABASE=SAMPLE;HOSTNAME=localhost;PORT=50000;UID=db2inst1;PWD=password", "", ""); print("Connected!")'
python -c "import ibm_db; conn = ibm_db.connect('DATABASE=SAMPLE;HOSTNAME=localhost;PORT=50000;UID=db2inst1;PWD=pass\!word', '', ''); print('Connected\!')"
db2 list active databases
./dist/my_app/my_app 'DATABASE=SAMPLE;HOSTNAME=localhost;PORT=50000;UID=db2inst1;PWD=password'
Remember: If import test passes, PyInstaller is working correctly. Connection failures are database/network issues.
4. Problem: "Permission denied" when running executable
Cause: Executable doesn't have execute permissions
Solution:
chmod +x dist/my_app/my_app
./dist/my_app/my_app
5. Problem: Executable works on build machine but fails on target machine
Cause: Missing system libraries on target machine
Solution:
ldd dist/my_app/my_app
sudo apt-get install libglib2.0-0 libsm6 libxext6 libxrender1
Additional Resources
Note: This solution was developed through extensive debugging and testing on production Linux systems. The approach has been validated with DB2 v11.5 and Python 3.8-3.13.