PostgreSQL raises ERROR: SRF protocol violated with SQLSTATE 39P02 and condition name srf_protocol_violated when a set-returning function (SRF) breaks the calling convention that PostgreSQL expects for functions that return multiple rows. The error message typically appears as:
ERROR: SRF protocol violated
What This Error Means
SQLSTATE class 39 covers "External Routine Invocation Exception" errors — problems that arise when PostgreSQL calls a function that does not behave as expected. Code 39P02 is a PostgreSQL-specific (the P prefix) extension within that class, reserved for violations of the set-returning function protocol.
A set-returning function (SRF) is any function declared to RETURN SETOF <type> or RETURN TABLE(...). PostgreSQL has two internal models for how SRFs produce rows:
- ValuePerCall mode — the function is called repeatedly by the executor, returning one row per call. Functions using this model must manage state between calls using a
FuncCallContext(in C) or equivalent mechanism. The function signals completion by returningSRF_RETURN_DONE(funcctx). - Materialize mode — the function fills a
Tuplestorewith all output rows on the first call. PostgreSQL then reads from that store.
39P02 is raised when a function written in C (or a procedural language extension) violates the contract of whichever mode it declared. The most common scenario is a C-level SRF that misuses SRF_IS_FIRSTCALL(), SRF_PERCALL_SETUP(), or the FuncCallContext lifecycle — for example, returning a valid row after the function has already signalled completion, or failing to initialize context on the first call.
After this error occurs, the current transaction is aborted. Any open transaction must be rolled back before new commands can be issued.
Common Causes
Bug in a C extension SRF. A user-defined C function implementing a set-returning function incorrectly manages the
FuncCallContext. For example, callingSRF_RETURN_NEXT()afterSRF_RETURN_DONE()has already been issued, or forgetting to callSRF_PERCALL_SETUP()on non-first calls.Incompatible extension compiled against a different PostgreSQL version. An extension
.sobinary built against a different major PostgreSQL version may have mismatched internal structs, causing the SRF calling sequence to go wrong. This often surfaces after a major-version upgrade when extensions are not recompiled.Procedural language bug in a PL/Python, PL/Perl, or PL/Tcl SRF. Some procedural language SRF wrappers (especially older or third-party ones) contain bugs that break the protocol under certain conditions — for example, a generator function that raises an exception mid-iteration without properly cleaning up.
Recursive or re-entrant call into the same SRF. Attempting to call an SRF recursively in a context where its
FuncCallContextis already live can corrupt state and trigger a protocol violation.
How to Fix srf_protocol_violated
Identify the offending function. The error message itself rarely names the function. Check the full error context:
-- After the error, the transaction is aborted. Roll back first: ROLLBACK; -- Re-run the query with verbose messaging to see the full context: \set VERBOSITY verbose SELECT your_srf_function();Recompile or reinstall the extension against the current PostgreSQL version. If the error appeared after a PostgreSQL upgrade, rebuild the extension from source:
# Example for a typical extension cd /path/to/extension-source make clean make make installThen reconnect and retest.
Update or replace the extension. Check whether a newer release of the extension fixes SRF protocol bugs. Many SRF bugs in third-party extensions have been corrected in later versions:
-- Check currently installed extension versions SELECT name, default_version, installed_version FROM pg_available_extensions WHERE installed_version IS NOT NULL;Fix the C function if you own the code. In a custom C SRF using ValuePerCall mode, ensure the standard pattern is followed exactly:
Datum my_srf(PG_FUNCTION_ARGS) { FuncCallContext *funcctx; if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* initialize state here */ MemoryContextSwitchTo(oldcontext); } funcctx = SRF_PERCALL_SETUP(); if (/* more rows available */) { /* build datum */ SRF_RETURN_NEXT(funcctx, result); } else { SRF_RETURN_DONE(funcctx); } }Never call
SRF_RETURN_NEXTorSRF_RETURN_DONEoutside this pattern, and never callSRF_RETURN_NEXTafterSRF_RETURN_DONE.Check PL/Python or PL/Perl SRFs for exception handling gaps. If your SRF is written in a procedural language and raises an exception mid-iteration, ensure the generator or iterator is properly closed/cleaned up before re-raising.
Additional Information
- SQLSTATE class
39("External Routine Invocation Exception") includes39001(invalid SQLSTATE returned),39004(null value not allowed), and39P01(trigger protocol violated) as related codes.39P02specifically targets SRF protocol violations. - This error is almost exclusively seen with C-language extensions or procedural language SRF wrappers. Pure SQL and PL/pgSQL
RETURN NEXT/RETURN QUERYfunctions do not use the C-levelFuncCallContextprotocol and will not produce this error. - The error was formalized in PostgreSQL as part of the standardization of SRF calling conventions. The ValuePerCall model has been stable since PostgreSQL 7.x; Materialize mode (
SFRM_Materialize) was added to give functions a simpler alternative. - After this error, the connection itself is not dropped, but the current transaction is in an aborted state. Issue
ROLLBACKbefore continuing. - PostgreSQL drivers (libpq, JDBC, psycopg2, etc.) will surface this as a standard server error with SQLSTATE
39P02. No special driver-level handling is needed or common.
Frequently Asked Questions
Why does this error only appear after I upgraded PostgreSQL?
Extensions with C-language SRFs must be compiled against the exact PostgreSQL major version they run under. Internal structures used by the SRF calling convention (such as FuncCallContext) can change between major versions. An extension .so built for PostgreSQL 14 may misread those structures on PostgreSQL 16, causing a protocol violation at runtime. Always recompile or reinstall extensions after a major-version upgrade.
Can a PL/pgSQL function trigger this error?
No. PL/pgSQL RETURN NEXT and RETURN QUERY are implemented by the PL/pgSQL interpreter, which correctly manages the underlying protocol on behalf of your code. 39P02 is specific to C-level or language-extension SRFs that directly interact with FuncCallContext or the SRF value-per-call machinery.
The error gives no function name. How do I find which function is causing it?
Run \set VERBOSITY verbose in psql before executing the failing query. The verbose output includes an internal context trace that often names the C function or extension. You can also check pg_stat_activity for the query text and cross-reference it with the SRFs it calls.
Is there a way to catch this error in a PL/pgSQL exception block? Yes. You can catch it using the condition name:
BEGIN
PERFORM my_srf_function();
EXCEPTION
WHEN srf_protocol_violated THEN
RAISE NOTICE 'SRF protocol error caught';
END;
However, catching the error does not fix the underlying bug — use this only as a temporary diagnostic measure.