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_Oracle driver 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

Structure of the DPI-1047 error on AWS Graviton ARM instances and the python-oracledb Thin mode fix 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_Oracle anymore and use oracledb instead.)
  • 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 added strip() 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

Share𝕏f

Comments