[SQLAlchemy] Fixing the DPI-1047 Python Oracle DB Connection Error on AWS Graviton (ARM) | SQLAlchemy, oracledb
1. The issue when changing to AWS EC2 ARM architecture
Recently, more companies are switching to the cost-effective AWS Graviton (ARM/aarch64) instances. But if you move Python code that worked fine on the existing x86_64 environment as-is, you'll run into the DPI-1047 error when integrating with Oracle DB.
I want to organize the fundamental cause of this error and a complete solution using the latest driver, python-oracledb.
2. What is the DPI-1047 error?
This error, which occurs 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 does it happen only on ARM servers?
This problem occurs because of a mismatch between the server CPU architecture and the library.
- cx_Oracle's dependency: the existing cx_Oracle driver only works if the C-based Oracle Instant Client library is installed on the system. (This way of integrating by installing the Client is called Thick mode.)
- Architecture mismatch: Oracle long did not officially support the Instant Client library for ARM (aarch64), or the setup was very tricky. In the end, trying to load an x86_64-only library on an ARM environment, the system doesn't recognize it — that's the architecture-mismatch problem.
(Beyond CPU architecture, Oracle DB also depends on various versions when installing — for example, RHEL or CentOS-based is much more convenient than Ubuntu. In my experience, Oracle DB was heavily affected by environmental factors.)
4. The fix: python-oracledb 'Thin' mode
Figure 1. The DPI-1047 error on an AWS Graviton ARM instance and python-oracledb Thin mode
To solve this architecture problem, Oracle released python-oracledb, the successor to cx_Oracle. In particular, this driver's Thin mode is an essential element when operating a Python server on an ARM server. (The way of integrating within the library itself, without needing the Instant Client, is called Thin mode.)
- No Instant Client needed: rewritten in 100% pure Python with no C library dependency.
- Architecture Agnostic: doesn't depend on the architecture. It works the same whether x86 or ARM. (That's why these days ox_Oracle is hardly used and only oracledb is used.)
- Direct communication: it talks to the DB directly via the Oracle Net Protocol, without going through system libraries.
| Driver | Characteristics | Recommended environment | SQLAlchemy URL scheme |
|---|---|---|---|
| cx_Oracle | C library-based | x86_64 (legacy) | oracle+cx_oracle:// |
| oracledb | Thin mode support | ARM(aarch64), Cloud | oracle+oracledb:// |
4.1 SQLAlchemy example and setup
1. Install the library
pip install oracledb
2. Code implementation examples
2.1 SQLAlchemy URL integration
When using cx_Oracle, you can see it starts with oracle+cx_oracle:// as below.
# [x86_64 only / high chance of error on ARM]
# the way that 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 (the recommended way when integrating via a SQLAlchemy URL)
from sqlalchemy import create_engine
# Removing 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 URL scheme for python-oracledb Thin mode
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!")
2.2 Native integration (calling the Driver Library)
1. The cx_Oracle way (the traditional Thick mode way)
Because it depends on a C library, it needs a process of initializing the library path at the code level, and if the architecture doesn't match, the error blows up right here. (cx_Oracle way: complex initialization)
import cx_Oracle
import os
# you have to load the instantclinet
lib_dir = "/opt/oracle/instantclient_21_10"
try:
# manual library initialization (a required process for Thick mode)
cx_Oracle.init_oracle_client(lib_dir=lib_dir)
except Exception as e:
print(f"Library load failed (DPI-1047 raised): {e}")
# connection info
dsn = cx_Oracle.makedsn("host", 1521, service_name="sn")
conn = cx_Oracle.connect(user="user", password="password", dsn=dsn)
2. python-oracledb (the latest Thin mode way) - (the recommended way)
You don't even need to specify a library path at all. It works with pure Python code regardless of architecture.
import oracledb
try:
# connect right away with no separate setup (Thin mode is the 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}")
As you can see, cx_Oracle has the hassle of having to directly call the Oracle Client library installed on the system, but python-oracledb omits that process entirely. When using the oracledb driver, no separate library initialization process (init_oracle_client) is needed at all, and it can run immediately regardless of x86 or ARM architecture.
When using AWS EC2 with the ARM architecture, the python-oracledb driver is an essential element for Python–Oracle DB integration.
📦 Migrated from the Tistory blog I used to run. Original: taehyuklee.tistory.com/32
Comments