When an external routine — a trigger body, PL/Python function, PL/Perl function, or another procedural language extension — raises an unhandled or uncategorized error, PostgreSQL surfaces it with SQLSTATE 39000 and the condition name external_routine_invocation_exception. The message typically appears as ERROR: <message from the external routine> with a detail or context line indicating which function or trigger was executing.
What This Error Means
SQLSTATE class 39 covers External Routine Invocation Exception, defined in the SQL standard to capture errors that occur specifically when the database engine calls out to code running outside the core SQL execution engine. PostgreSQL maps this class to errors that arise inside procedural language handlers (PL/pgSQL is excluded — it uses class P0), external C functions, or language bridges such as PL/Python, PL/Perl, PL/Java, and PL/v8.
39000 is the parent (generic) code for the class. PostgreSQL does not define many child codes under class 39 the way it does for class 22 (data exceptions), so in practice 39000 is what you see when none of the more-specific sub-codes apply. The SQL standard defines a handful of children (39001 INVALID SQLSTATE RETURNED, 39004 NULL VALUE NOT ALLOWED, etc.), but PostgreSQL's own catalog lists only 39001 and the catch-all 39000.
When this error is raised, the current transaction is marked as aborted. No further SQL statements will execute until the application issues a ROLLBACK (or the connection framework does so automatically). The external routine's side effects are rolled back along with the rest of the transaction, provided the routine did not interact with external systems outside the database.
Common Causes
An unhandled Python/Perl/Java exception inside a PL function. If a PL/Python function raises a Python exception that is not caught inside the function body, PostgreSQL converts it to SQLSTATE
39000and propagates the Python traceback in theDETAILorCONTEXTfields.A trigger written in an external language raising an error. A
BEFORE INSERTtrigger implemented in PL/Perl that callselog(ERROR, ...)or Perl'sdiewill surface as39000when the triggering DML statement is executed.A NULL argument passed to an external routine that declares
STRICT. Some external language functions useAS $$ ... $$ LANGUAGE plpython3u STRICT;. If all arguments are non-NULL but the function body still encounters an unexpected NULL from within, PostgreSQL may surface it through this class.A misconfigured or missing external language extension. If the procedural language handler itself fails during initialization (e.g., the Python interpreter cannot be loaded, or a required module is absent), the invocation exception is raised before any user code executes.
A C extension function signaling an error via
ereport(ERROR, ...)without a specific SQLSTATE. Custom C extensions that callereportwithout assigning a SQLSTATE will default toXX000(internal error), but improperly constructed extensions can land in class39.
How to Fix external_routine_invocation_exception
Inspect the full error context. The SQLSTATE
39000message on its own is vague. Always check theCONTEXTandDETAILfields. In psql, run with\set VERBOSITY verboseto see all fields:\set VERBOSITY verbose -- Then re-run the failing statementIn application code, inspect the error's
pgcode,pgerror,diagstructure (exact attribute names depend on the driver).Add error handling inside the external routine. For PL/Python, wrap risky code in a
try/exceptblock and useplpy.error()orplpy.fatal()to emit structured errors:CREATE OR REPLACE FUNCTION safe_lookup(input text) RETURNS text LANGUAGE plpython3u AS $$ try: result = some_operation(input) return result except Exception as e: plpy.error(f"safe_lookup failed: {e}") $$;For PL/Perl, replace bare
diewithelog. A baredie "something went wrong"in PL/Perl will produce a generic39000. Usingelog(ERROR, "message")lets you attach a cleaner error message:CREATE OR REPLACE FUNCTION check_value(val integer) RETURNS boolean LANGUAGE plperl AS $$ my $v = shift; if ($v < 0) { elog(ERROR, "check_value: negative values not allowed"); } return 't'; $$;Verify the procedural language is correctly installed. Confirm the language handler is present and the shared library is loadable:
SELECT * FROM pg_language WHERE lanname = 'plpython3u';If missing, install it with
CREATE EXTENSION plpython3u;and ensure the Python shared library is available on the server host.Check the PostgreSQL server log for the full traceback. For PL/Python errors, the Python traceback is written to the server log at
log_min_messages = DEBUG1or above, even if it is not returned to the client.
Additional Information
- SQLSTATE class
39is defined in the ISO SQL standard; PostgreSQL follows it closely for external language handlers. - Related sibling code:
39001(INVALID_SQLSTATE_RETURNED) is raised when an external routine returns a SQLSTATE that PostgreSQL does not recognize. - PL/pgSQL errors use class
P0(PLPGSQL_ERROR,P0001forRAISE, etc.) — not class39. If you seeP0001orP0002, that is a different error class. - Most PostgreSQL client drivers (psycopg2, psycopg3, asyncpg, JDBC, node-postgres) expose the SQLSTATE as a property on the exception object. For
39000, psycopg2 raisespsycopg2.errors.ExternalRoutineInvocationException. - Because class
39errors abort the current transaction, connection poolers (PgBouncer, RDS Proxy) will not recycle the connection until the application sendsROLLBACK. Failure to roll back can exhaust the pool. - This error class has been part of PostgreSQL's SQLSTATE mapping since at least PostgreSQL 8.0.
Frequently Asked Questions
Why does my PL/Python function show SQLSTATE 39000 but no useful message?
The Python traceback is often written to the PostgreSQL server log rather than returned to the client. Connect to the server and check the log file (typically in $PGDATA/log/), or temporarily set log_min_messages = DEBUG1 in postgresql.conf and reload. Alternatively, wrap your Python code in try/except and call plpy.error(str(e)) to surface the message explicitly.
Is 39000 the same as a PL/pgSQL RAISE EXCEPTION?
No. RAISE EXCEPTION in PL/pgSQL produces SQLSTATE P0001 (RAISE_EXCEPTION), which belongs to class P0 (PL/pgSQL error). Class 39 is reserved for external procedural languages (PL/Python, PL/Perl, PL/Java, C extensions, etc.).
Can I catch SQLSTATE 39000 inside a PL/pgSQL BEGIN/EXCEPTION block?
Yes. You can catch it by condition name or SQLSTATE:
BEGIN
PERFORM my_plpython_function();
EXCEPTION
WHEN external_routine_invocation_exception THEN
-- handle or log the error
RAISE NOTICE 'External routine failed: %', SQLERRM;
END;
What should I do if the error occurs inside a trigger?
Identify which trigger is failing by examining the CONTEXT in the error message — it will name the trigger and table. Then review that trigger's function for unhandled exceptions. Adding logging inside the trigger function (e.g., plpy.log(...) in PL/Python) can help narrow down the exact failure point without aborting the transaction.