PostgreSQL SRF Protocol Violated (SQLSTATE 39P02)

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 returning SRF_RETURN_DONE(funcctx).
  • Materialize mode — the function fills a Tuplestore with 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

  1. Bug in a C extension SRF. A user-defined C function implementing a set-returning function incorrectly manages the FuncCallContext. For example, calling SRF_RETURN_NEXT() after SRF_RETURN_DONE() has already been issued, or forgetting to call SRF_PERCALL_SETUP() on non-first calls.

  2. Incompatible extension compiled against a different PostgreSQL version. An extension .so binary 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.

  3. 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.

  4. Recursive or re-entrant call into the same SRF. Attempting to call an SRF recursively in a context where its FuncCallContext is already live can corrupt state and trigger a protocol violation.

How to Fix srf_protocol_violated

  1. 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();
    
  2. 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 install
    

    Then reconnect and retest.

  3. 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;
    
  4. 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_NEXT or SRF_RETURN_DONE outside this pattern, and never call SRF_RETURN_NEXT after SRF_RETURN_DONE.

  5. 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") includes 39001 (invalid SQLSTATE returned), 39004 (null value not allowed), and 39P01 (trigger protocol violated) as related codes. 39P02 specifically 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 QUERY functions do not use the C-level FuncCallContext protocol 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 ROLLBACK before 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.

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.