PostgreSQL Error 0Z002: Stacked Diagnostics Accessed Without Active Handler
PostgreSQL error 0Z002 occurs when GET STACKED DIAGNOSTICS is called outside of an active exception handler (EXCEPTION block) in PL/pgSQL. This statement is exclusively designed to retrieve exception context information—such as error messages, SQL states, and stack traces—and is only valid when an exception is actively being handled. If you call it anywhere else in your function body, PostgreSQL raises this error immediately.
Top 3 Causes
1. Calling GET STACKED DIAGNOSTICS Outside an EXCEPTION Block
This is the most common cause. Developers mistakenly place the statement in the main body of a function instead of inside the EXCEPTION handler.
-- WRONG: causes 0Z002
CREATE OR REPLACE FUNCTION bad_func()
RETURNS void AS $$
DECLARE
v_message TEXT;
BEGIN
-- This will raise 0Z002 immediately
GET STACKED DIAGNOSTICS v_message = MESSAGE_TEXT;
RAISE NOTICE '%', v_message;
END;
$$ LANGUAGE plpgsql;
-- CORRECT: GET STACKED DIAGNOSTICS inside EXCEPTION block
CREATE OR REPLACE FUNCTION good_func()
RETURNS void AS $$
DECLARE
v_message TEXT;
v_context TEXT;
BEGIN
PERFORM 1 / 0; -- trigger an exception
EXCEPTION
WHEN OTHERS THEN
GET STACKED DIAGNOSTICS
v_message = MESSAGE_TEXT,
v_context = PG_EXCEPTION_CONTEXT;
RAISE NOTICE 'Error: %, Context: %', v_message, v_context;
END;
$$ LANGUAGE plpgsql;
2. Incorrect Scope in Nested Blocks
When using nested BEGIN...END blocks, it's easy to lose track of which block's exception handler is active. Calling GET STACKED DIAGNOSTICS in an outer block's normal execution area after handling an inner exception is a common mistake.
-- CORRECT: scoped properly within the inner EXCEPTION block
CREATE OR REPLACE FUNCTION nested_example()
RETURNS void AS $$
DECLARE
v_message TEXT;
BEGIN
RAISE NOTICE 'Outer block running';
BEGIN
-- inner block triggers exception
PERFORM 1 / 0;
EXCEPTION
WHEN division_by_zero THEN
-- correct: inside the active handler
GET STACKED DIAGNOSTICS v_message = MESSAGE_TEXT;
RAISE NOTICE 'Caught inner error: %', v_message;
END;
-- outer block continues normally here
RAISE NOTICE 'Outer block continues';
END;
$$ LANGUAGE plpgsql;
3. Confusing GET DIAGNOSTICS with GET STACKED DIAGNOSTICS
GET DIAGNOSTICS is for general runtime information (e.g., row counts) and can be used anywhere. GET STACKED DIAGNOSTICS is exclusively for exception context. Mixing them up leads directly to 0Z002.
CREATE OR REPLACE FUNCTION diagnostics_example()
RETURNS void AS $$
DECLARE
v_rows BIGINT;
v_message TEXT;
BEGIN
UPDATE my_table SET status = 'active' WHERE id = 1;
-- GET DIAGNOSTICS: valid outside EXCEPTION block
GET DIAGNOSTICS v_rows = ROW_COUNT;
RAISE NOTICE 'Rows updated: %', v_rows;
EXCEPTION
WHEN OTHERS THEN
-- GET STACKED DIAGNOSTICS: only valid here
GET STACKED DIAGNOSTICS v_message = MESSAGE_TEXT;
RAISE WARNING 'Error occurred: %', v_message;
END;
$$ LANGUAGE plpgsql;
Quick Fix Solutions
- Always place
GET STACKED DIAGNOSTICSinside anEXCEPTION WHEN ... THENblock. - Use
GET DIAGNOSTICS(withoutSTACKED) for runtime context likeROW_COUNToutside exception handlers. - For complex functions, use a dedicated error logging pattern:
CREATE OR REPLACE FUNCTION safe_operation()
RETURNS void AS $$
DECLARE
v_state TEXT;
v_message TEXT;
v_context TEXT;
BEGIN
-- your business logic here
PERFORM risky_function();
EXCEPTION
WHEN OTHERS THEN
GET STACKED DIAGNOSTICS
v_state = RETURNED_SQLSTATE,
v_message = MESSAGE_TEXT,
v_context = PG_EXCEPTION_CONTEXT;
INSERT INTO error_log(sql_state, message, context, logged_at)
VALUES (v_state, v_message, v_context, NOW());
RAISE; -- re-raise if needed
END;
$$ LANGUAGE plpgsql;
Prevention Tips
-
Use
plpgsql_check: Install theplpgsql_checkextension and runSELECT * FROM plpgsql_check_function('your_func()');as part of your CI/CD pipeline to catch misplaced diagnostics calls before deployment.
CREATE EXTENSION IF NOT EXISTS plpgsql_check;
SELECT * FROM plpgsql_check_function('safe_operation()');
-
Adopt a standard function template: Establish a team-wide PL/pgSQL template where
GET STACKED DIAGNOSTICSalways lives inside theEXCEPTIONblock. This structural habit eliminates the error class entirely and enforces consistent error handling across your codebase.
Related Error Codes
-
0Z000— Parent classdiagnostics_exception, the broader category this error belongs to. -
P0000— Generalplpgsql_errorfor PL/pgSQL runtime issues. -
P0001—raise_exception, commonly seen alongside stacked diagnostics usage.
📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.
Top comments (0)