Python ibm_db on IBM z/OS UNIX (USS): a field‑ready install & troubleshooting guide
Running Python against Db2 on z/OS is powerful—but a little different from Linux/Windows. This post walks you through a proven setup for the ibm_db driver on z/OS UNIX (USS), how to avoid the most common pitfalls, and what to check when something goes sideways.
TL;DR
- Install IBM Open Enterprise SDK for Python for z/OS and get ibm_db from the IBM AI Python Toolkit for z/OS repository. [ibm.com], [ibm.com]
- On z/OS, ODBC attaches locally to Db2 via CAF or RRSAF—not a TCP/IP “type 4” hop—so the Python connection string should be empty ("") and behavior is driven by DSNAOINI. [ibm.com], [ibm.com]
- Ensure STEPLIB and DSNAOINI are right; encode your INI in IBM‑1047 (with [=0xAD, ]=0xBD) and make sure T=off. [ibm.com], [ibm.com], [ibm.com]
Prereqs (official names & where to get them)
- Python runtime on z/OS
Install IBM Open Enterprise SDK for Python for z/OS (the supported, productized Python on z/OS). [ibm.com]
- Where to get ibm_db
Use the IBM AI Python Toolkit for z/OS as the curated repository of prebuilt Python packages for z/OS, including ibm_db. [ibm.com], [redbooks.ibm.com]
Install
From USS, point pip to the IBM AI Toolkit index and install ibm_db wheels for z/OS:
pip3 install \
--index-url https://downloads.pyaitoolkit.ibm.net/repository/python_ai_toolkit_zos/simple \
--trusted-host downloads.pyaitoolkit.ibm.net \
--only-binary :all: \
ibm_db
The AI Toolkit is IBM’s vetted package repo for z/OS; it’s the recommended way to consume Python packages like ibm_db on z/OS. [redbooks.ibm.com]
Two environment variables you must set on z/OS
1) STEPLIB
Concatenate your Db2 libraries, SDSNEXIT then SDSNLOAD, and include SDSNLOD2 (XPLINK/64-bit ODBC DLLs). Example:
export STEPLIB=DSN1310.SDSNEXIT:DSN1310.SDSNLOAD:DSN1310.SDSNLOD2
IBM’s ODBC execution guidance for z/OS UNIX explicitly calls out STEPLIB and the need for SDSNEXIT/SDSNLOAD; it also notes the XPLINK/64‑bit ODBC libraries in SDSNLOD2 (e.g., DSNAOCLX, DSNAO64C). [ibm.com]
2) DSNAOINI
Point to your Db2 ODBC initialization file (can be HFS/USS file or a dataset):
export DSNAOINI="/u/yourid/odbc/dsnaoini"
# or: export DSNAOINI="USER1.DB2ODBC.DATA(ODBCINI)"
DSNAOINI drives ODBC runtime behavior. You can supply it via the environment variable or a DD name; USS paths and z/OS datasets are both supported. [ibm.com]
A minimal, working DSNAOINI (and why these settings matter)
Start with a COMMON section like this:
[COMMON]
MVSDEFAULTSSID=<attach>
CONNECTTYPE=1
MULTICONTEXT=2
CURRENTAPPENSCH=ASCII
FLOAT=IEEE
MVSATTACHTYPE=CAF ; or RRSAF
- MVSDEFAULTSSID defines the local Db2 subsystem to attach to. If missing, ODBC uses the DSNHDECP default from SDSNEXIT. [ibm.com]
- MVSATTACHTYPE governs how ODBC attaches: CAF or RRSAF (both are local attachment facilities, not network). [ibm.com]
- CURRENTAPPENSCH=ASCII: ibm_db originated on ASCII platforms; setting ASCII avoids mangled messages in edge cases. Technically, CURRENTAPPENSCH is an ODBC initialization keyword that influences how character data is bound when SQL_C_WCHAR isn’t used; using ASCII keeps the app‑encoding expectations aligned with the driver. (Your SQL data still comes back to Python as Unicode.) [ibm.com], [ibm.com]
Encoding gotcha (critical): The file’s code page must be EBCDIC and correctly tagged for ODBC to parse it. IBM’s troubleshooting doc shows that if DSNAOINI is mis‑tagged as “text” or the encoding is wrong, ibm_db.connect() may fail before any ODBC trace is even created. Use chtag -p to verify T=off and that the file is binary or mixed IBM‑1047. [ibm.com]
Verify the bracket bytes in IBM‑1047
Because the INI uses section headers like [COMMON], you must ensure the [ and ] characters are the right EBCDIC code points for IBM‑1047: [ = 0xAD, ] = 0xBD. (Many EBCDIC CCSIDs place these at different hex values.) Use HEX ON in ISPF or od -ctx from USS to double‑check. [ibm.com]
Connecting from Python (empty string is your friend)
On z/OS, the ODBC driver attaches locally to the Db2 subsystem in your LPAR using CAF or RRSAF. That means you are not making a TCP/IP “type 4” hop; network keywords like HOSTNAME/PORT in the connection string don’t apply to z/OS ODBC. Use an empty connection string and let DSNAOINI/DSNHDECP drive the attach:
import ibm_db
# Optional: turn on client-side debug logging
ibm_db.debug(True)
# Local attach via CAF/RRSAF – connection info comes from DSNAOINI/DSNHDECP
conn = ibm_db.connect("", "", "")
IBM documents that the Db2 ODBC driver on z/OS uses CAF or RRSAF to connect to the local Db2 address space; remote access is through Db2 itself (three‑part names/aliases over DRDA), not via a networked ODBC hop from your Python process. Hence, keep the connection string empty on z/OS. [ibm.com]
Quick checklist (don’t skip any)
- STEPLIB set (EXIT, LOAD, LOD2):
echo $STEPLIB → should show …SDSNEXIT:…SDSNLOAD:…SDSNLOD2. [ibm.com]
- DSNAOINI exported:
echo $DSNAOINI → path/dataset resolves. [ibm.com]
- Subsystem matches:
grep MVSDEFAULTSSID $DSNAOINI → SSID you intend, consistent with STEPLIB/EXIT. [ibm.com]
- App encoding:
grep CURRENTAPPENSCH $DSNAOINI → ASCII. (Prevents message corruption in mixed-encoding contexts.) [ibm.com]
- Tagging:
chtag -p $DSNAOINI → T=off and binary or m IBM‑1047. [ibm.com]
- Hex for [ and ]:
head -5 $DSNAOINI | od -ctx → [=0xAD, ]=0xBD (IBM‑1047). [ibm.com]
- Empty connection string:
In Python, call ibm_db.connect("", "", ""). (Let DSNAOINI/DSNHDECP drive the local attach.) [ibm.com]
The #1 error I see (and how to fix it)
File "/u/user/script.py", line 5, in main
conn = ibm_db.connect(conn_string, "", "")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc0 in position 0: invalid start byte
This typically means your DSNAOINI is missing, incorrect, encoding, and/or tagging is wrong (for example, an EBCDIC file being read as UTF‑8). Re‑run every step in the checklist—especially chtag -p and the hex check for [ ]. IBM’s troubleshooting guide calls out the text‑tag problem explicitly. [ibm.com]
If you still get errors, turn on ibm_db client debug and enable ODBC traces:
import ibm_db
ibm_db.debug(True)
conn = ibm_db.connect("", "", "")
And in DSNAOINI:
[COMMON]
APPLTRACE=1
APPLTRACEFILENAME=/u/yourid/odbc/appltrace.txt
DIAGTRACE=1
DIAGTRACE_BUFFER_SIZE=2000000
IBM’s docs describe enabling APPLTRACE/DIAGTRACE and where the trace files should go; they also provide tooling (DSNAOTRC/DSNAO64T) for formatting the traces. [ibm.com], [ibm.com]
For deeper issues, consult IBM’s “Troubleshooting IBM_DB Python driver support through Db2 for z/OS ODBC”—it’s the authoritative checklist for package binds, INI format, and tracing. [ibm.com], [ibm.com]
Why the empty connection string matters on z/OS
On distributed platforms, you often pass HOSTNAME, PORT, PROTOCOL etc. In contrast, z/OS ODBC is a local attach into your LPAR’s Db2 (CAF/RRSAF). If you try to smuggle network keywords into ibm_db.connect() on z/OS, you’ll just create confusion—those values aren’t used by the z/OS ODBC driver. Keep it empty and let DSNAOINI + DSNHDECP do their job. [ibm.com], [ibm.com]
A word on encoding and CURRENTAPPENSCH
- CURRENTAPPENSCH in DSNAOINI tells ODBC how to interpret application character data when it binds parameters/columns as single-byte chars. Setting ASCII aligns with the ibm_db runtime expectations; if you switch this, you can stumble into message corruption even though result sets still arrive in Python as Unicode. [ibm.com]
- INI code page: Ensure the INI itself is in an EBCDIC CCSID and not tagged as “text”. If you choose IBM‑1047, remember the special case for bracket characters: [=0xAD, ]=0xBD. (Other EBCDIC CCSIDs map these differently.) [ibm.com], [ibm.com]
Appendix: How ODBC on z/OS actually connects (CAF/RRSAF)
The Db2 ODBC runtime on z/OS is a dynamic library loaded in your address space. It then attaches to Db2 using CAF or RRSAF (selectable with MVSATTACHTYPE), and it can reach remote data via three‑part names/aliases over DRDA through Db2, not by having your Python process open a TCP socket to a remote server. [ibm.com]
References