Fixing the DPI-1047 Python–Oracle Error on AWS Graviton (ARM) — python-oracledb Thin Mode
1. The Issue When Migrating AWS EC2 to ARM
More companies are switching to cost-effective AWS Graviton (ARM/aarch64) instances. But moving Python code that worked on x86_64 as-is, you hit the DPI-1047 error when connecting to Oracle DB.
This post covers the root cause and the fix using the latest driver, python-oracledb.
2. What is the DPI-1047 error?
This error, raised when connecting via SQLAlchemy or cx_Oracle, means "the Oracle Client library cannot be located."
sqlalchemy.exc.DatabaseError: (cx_Oracle.DatabaseError) DPI-1047: Cannot locate a 64-bit Oracle Client library:
"libclntsh.so: cannot open shared object file: No such file or directory".
3. Cause — Why Only on ARM Servers?
This happens because of a mismatch between the server CPU architecture and the library.
- cx_Oracle's dependency — the older
cx_Oracledriver only works if the C-based Oracle Instant Client library is installed on the system. (Connecting by installing the client like this is called Thick mode.) - Architecture mismatch — Oracle long did not officially support (or made it very fiddly to configure) the Instant Client for ARM (aarch64). Trying to load an x86_64-only library on ARM, the system can't recognize it. (Beyond CPU architecture, Oracle DB is also sensitive to environment factors — e.g., RHEL/CentOS is far easier than Ubuntu for installation.)
4. The Fix — python-oracledb 'Thin' Mode
Figure 1. DPI-1047 (architecture mismatch) → python-oracledb Thin mode fix
To solve this architecture problem, Oracle released python-oracledb, the successor to cx_Oracle. Its Thin mode is essential for running a Python server on ARM. (Connecting from within the library without an Instant Client is called Thin mode.)
- No Instant Client needed — rewritten as 100% pure Python with no C-library dependency.
- Architecture agnostic — it doesn't care about the architecture; it works identically on x86 or ARM. (Hence people rarely use
cx_Oracleanymore and useoracledbinstead.) - Direct communication — talks to the DB directly over the Oracle Net Protocol, without going through system libraries.
| Driver | Characteristics | Recommended for | SQLAlchemy URL scheme |
|---|---|---|---|
cx_Oracle |
C-library based | x86_64 (legacy) | oracle+cx_oracle:// |
oracledb |
Thin mode support | ARM (aarch64), Cloud | oracle+oracledb:// |
First, install the library.
pip install oracledb
4.1 SQLAlchemy URL Integration
With cx_Oracle, the URL starts with oracle+cx_oracle:// as below.
# [x86_64 only / high chance of error on ARM]
# It hunts for the system's libclntsh.so file
SQLALCHEMY_DATABASE_URL = f"oracle+cx_oracle://{user}:{password}@{host}:{port}/?service_name={service_name}"
The oracledb way (recommended — SQLAlchemy URL integration):
from sqlalchemy import create_engine
# Stripping invisible whitespace when loading from JSON/env vars is a must
user = db.get("user").strip()
password = db.get("password").strip()
host = db.get("host").strip()
port = db.get("port")
service_name = db.get("service_name").strip()
# Use the python-oracledb Thin mode URL scheme
SQLALCHEMY_DATABASE_URL = f"oracle+oracledb://{user}:{password}@{host}:{port}/?service_name={service_name}"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
# Connection test
with engine.connect() as conn:
print("Connected to Oracle DB successfully on ARM(aarch64) server!")
💡 Tip — when loading info from JSON or
.env, invisible whitespace or newlines (\n) can cause connection failures — especially when the password ends with a special character like!. That's why I addedstrip()at the end.
4.2 Native Driver Integration
① cx_Oracle (the traditional Thick mode) — it depends on a C library, so you must initialize the library path at the code level, and if the architecture doesn't match, the error fires right here.
import cx_Oracle
import os
# Must load the Instant Client
lib_dir = "/opt/oracle/instantclient_21_10"
try:
# Manual library init (required for Thick mode)
cx_Oracle.init_oracle_client(lib_dir=lib_dir)
except Exception as e:
print(f"Library load failed (DPI-1047): {e}")
# Connection info
dsn = cx_Oracle.makedsn("host", 1521, service_name="sn")
conn = cx_Oracle.connect(user="user", password="password", dsn=dsn)
② python-oracledb (the modern Thin mode — recommended) — no library path needed at all. It runs as pure Python regardless of architecture.
import oracledb
try:
# Connect directly with no extra config (Thin mode by default)
conn = oracledb.connect(
user="user",
password="password",
host="host",
port=1521,
service_name="sn"
)
print("Connected in Thin mode!")
except Exception as e:
print(f"Connection failed: {e}")
cx_Oracle has the hassle of calling the Oracle Client library installed on the system, whereas python-oracledb skips that entirely. It needs no separate library init (init_oracle_client) and runs immediately regardless of x86/ARM architecture.
If you run AWS EC2 on ARM, the python-oracledb driver is essential for Python–Oracle DB integration.
📦 Migrated from my own Korean blog (my own writing). Original: taehyuklee.tistory.com/32
Comments