Complete List of Common Oracle Errors That Cause ORA-06512
- ORA-06512 is a stack trace indicator β never the actual error to fix; always parse the preceding ORA code
- Always parse the first non-06512 ORA code from the error stack for alerting and triage routing
- 30+ common Oracle errors trigger ORA-06512 β categorize by type (data integrity, cursor, logic, memory, security, conversion, concurrency, DML) for efficient triage
- ORA-06512 is a stack trace indicator, not an error itself β it shows where in the call stack the real error occurred
- The actual error always precedes ORA-06512 and determines the root cause; fix that code, not the stack frame
- 30+ common Oracle errors trigger this stack trace in production PL/SQL systems
- Performance insight: deeply nested exception stacks add 5β15 ms per propagation layer because Oracle must format and copy the backtrace string at every RAISE point β in a 12-layer-deep call chain that overhead becomes measurable in batch loops
- Production insight: monitoring tools that alert only on ORA-06512 miss the actual error code and generate noise proportional to total exception volume
- Biggest mistake: treating ORA-06512 as the error to fix instead of the preceding exception β this wastes hours because the stack frame itself is informational, never causal
Need to find the primary error in a stack trace
-- 19c+ ADR alert history
SELECT originating_timestamp, message_text
FROM v$diag_alert_ext
WHERE message_text LIKE '%ORA-06512%'
ORDER BY originating_timestamp DESC
FETCH FIRST 20 ROWS ONLY;-- Extract primary error code from a captured SQLERRM value
SELECT REGEXP_SUBSTR(error_message, 'ORA-[0-9]{5}') AS primary_error
FROM io.thecodeforge.logging.error_log
WHERE REGEXP_SUBSTR(error_message, 'ORA-[0-9]{5}') != 'ORA-06512'
ORDER BY created_at DESC
FETCH FIRST 20 ROWS ONLY;Need to identify which procedure raises the most ORA-06512 stacks
-- Top error-producing objects from your error log
SELECT object_name, error_line, primary_error_code,
COUNT(*) AS occurrences,
MAX(created_at) AS last_seen
FROM io.thecodeforge.logging.error_log
WHERE created_at > SYSDATE - 7
GROUP BY object_name, error_line, primary_error_code
ORDER BY occurrences DESC
FETCH FIRST 20 ROWS ONLY;-- Check for invalid objects that might be generating compilation errors
SELECT owner, object_name, object_type, status, last_ddl_time
FROM dba_objects
WHERE status = 'INVALID'
AND owner NOT IN ('SYS','SYSTEM')
ORDER BY last_ddl_time DESC;Need to correlate ORA-06512 with session activity at the time of the error
-- Correlate error timestamps with ASH
SELECT h.sample_time, h.sql_id, h.event, h.blocking_session,
h.module, h.action, h.program
FROM dba_hist_active_sess_history h
WHERE h.sample_time BETWEEN
TIMESTAMP '2026-04-14 01:15:00' AND
TIMESTAMP '2026-04-14 01:20:00'
AND h.session_id = &error_session_id
ORDER BY h.sample_time;-- Find the SQL text for the failing statement
SELECT sql_id, sql_text
FROM v$sql
WHERE sql_id = '&sql_id_from_ash'
FETCH FIRST 1 ROWS ONLY;Production Incident
Production Debug GuideSymptom-to-action mapping for stack trace debugging in production environments
ORA-06512 appears in nearly every PL/SQL error stack but is not the error itself. It indicates the line number and call stack position where the preceding exception was raised or propagated. Every time an unhandled exception crosses a procedure or function boundary, Oracle appends another ORA-06512 frame β which is why deep call chains produce long stacks. Production monitoring systems that alert on ORA-06512 without parsing the preceding error code generate false positives and miss root causes. This reference catalogs the most common Oracle errors that trigger ORA-06512 in production environments, organized by category for rapid triage. Each entry includes the error code, typical cause, severity, production frequency, and a link to the detailed debugging guide. Whether you are an on-call DBA triaging a midnight alert or a developer debugging a batch failure, start here to identify the real error behind the stack trace.
Understanding ORA-06512 Stack Traces
ORA-06512 is Oracle's stack trace indicator. It always accompanies another error and shows the call chain leading to the failure. The format is:
ORA-06512: at "[owner.]object_name", line [number]
Multiple ORA-06512 lines indicate nested calls β each line is one procedure boundary where the exception propagated without being caught. The actual error code appears before the first ORA-06512 line.
Under the hood, Oracle builds this stack incrementally as the exception unwinds through the call chain. Each time the runtime crosses a procedure boundary without encountering an exception handler, it appends a new ORA-06512 frame. This is why deeply nested architectures β especially those using layered validation, auditing, and business-rule packages β can produce stacks with 15 or more frames.
Production systems must parse the full stack to extract the primary error for alerting and triage. The function below handles edge cases including stacks where ORA-06512 appears first (which happens when SQLERRM truncates the preceding error) and stacks with multiple distinct errors (which happens with RAISE_APPLICATION_ERROR called inside a WHEN OTHERS handler).
-- Example error stack (read bottom-up for call order): -- ORA-01403: no data found -- ORA-06512: at "IO.THECODEFORGE.ORDER_PROCESSOR", line 142 -- ORA-06512: at "IO.THECODEFORGE.BATCH_RUNNER", line 87 -- ORA-06512: at line 1 -- -- Translation: anonymous block (line 1) called BATCH_RUNNER (line 87) -- which called ORDER_PROCESSOR (line 142), which raised ORA-01403. -- Robust primary error extractor CREATE OR REPLACE FUNCTION io.thecodeforge.util.parse_primary_error( p_error_stack IN VARCHAR2 ) RETURN VARCHAR2 DETERMINISTIC IS v_match VARCHAR2(20); v_pos PLS_INTEGER := 1; v_idx PLS_INTEGER := 0; BEGIN -- Walk through all ORA- codes in order and return the first -- one that is not ORA-06512 LOOP v_idx := v_idx + 1; v_match := REGEXP_SUBSTR( p_error_stack, 'ORA-[0-9]{5}', 1, v_idx ); EXIT WHEN v_match IS NULL; -- no more codes EXIT WHEN v_match != 'ORA-06512'; -- found the real error END LOOP; RETURN NVL(v_match, 'UNKNOWN'); -- defensive fallback END parse_primary_error; / -- Comprehensive exception handler pattern for production procedures CREATE OR REPLACE PROCEDURE io.thecodeforge.example.guarded_proc IS v_primary VARCHAR2(20); BEGIN -- ... business logic ... NULL; EXCEPTION WHEN OTHERS THEN v_primary := io.thecodeforge.util.parse_primary_error(SQLERRM); INSERT INTO io.thecodeforge.logging.error_log ( session_id, primary_error, full_stack, backtrace, object_name, error_line, call_stack, created_at ) VALUES ( SYS_CONTEXT('USERENV','SESSIONID'), v_primary, DBMS_UTILITY.FORMAT_ERROR_STACK, -- up to 2000 bytes DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, -- exact line trace $$PLSQL_UNIT, $$PLSQL_LINE, DBMS_UTILITY.FORMAT_CALL_STACK, -- who called whom SYSTIMESTAMP ); RAISE; -- ALWAYS re-raise after logging END guarded_proc; /
- The actual error (ORA-XXXXX) appears first in the stack, before any ORA-06512 line
- Each ORA-06512 line shows one procedure boundary where the exception propagated
- The lowest ORA-06512 (closest to 'at line 1') is the outermost caller β the entry point
- Line numbers point to the RAISE statement or the failing DML/SELECT in each procedure
- DBMS_UTILITY.FORMAT_ERROR_BACKTRACE preserves the original line even after re-raising β FORMAT_ERROR_STACK does not
Data Integrity Errors (ORA-00001 to ORA-02449)
Constraint violations and data integrity errors are the single most frequent source of ORA-06512 stacks in transactional systems. These errors indicate that a DML statement violated a table constraint β unique, foreign key, check, or NOT NULL. They surface business logic violations, data quality issues in upstream feeds, or race conditions in concurrent inserts.
The ORA-06512 stack trace reveals which procedure attempted the violating operation and the exact line number of the INSERT, UPDATE, or MERGE statement. Critically, the SQLERRM message for constraint violations includes the constraint name β extracting this gives you the exact column(s) involved without guessing.
In batch processing systems, a single constraint violation can abort an entire batch unless you use SAVEPOINT and partial-commit patterns. The code below demonstrates both individual and bulk handling approaches.
-- ORA-00001: unique constraint violation -- Pattern: individual row handling with constraint name extraction CREATE OR REPLACE PROCEDURE io.thecodeforge.etl.insert_order( p_order_id IN NUMBER, p_customer_id IN NUMBER, p_amount IN NUMBER ) IS v_constraint_name VARCHAR2(128); BEGIN INSERT INTO io.thecodeforge.orders ( order_id, customer_id, order_amount, created_at ) VALUES ( p_order_id, p_customer_id, p_amount, SYSTIMESTAMP ); EXCEPTION WHEN DUP_VAL_ON_INDEX THEN -- Extract constraint name from SQLERRM for precise diagnosis v_constraint_name := REGEXP_SUBSTR( SQLERRM, '\(([^)]+)\)', 1, 1, NULL, 1 ); io.thecodeforge.logging.log_error( p_error_type => 'UNIQUE_VIOLATION', p_message => 'Constraint: ' || v_constraint_name || ' OrderID: ' || p_order_id, p_stack => DBMS_UTILITY.FORMAT_ERROR_BACKTRACE ); -- Decide: skip duplicate or raise to caller RAISE; END insert_order; / -- ORA-02291: integrity constraint violated - parent key not found -- Pattern: pre-validation for FK relationships in batch loads CREATE OR REPLACE PROCEDURE io.thecodeforge.etl.load_order_items IS v_orphan_count NUMBER; BEGIN -- Identify orphaned staging records BEFORE attempting inserts INSERT INTO io.thecodeforge.staging.rejected_items ( order_id, product_id, reject_reason, rejected_at ) SELECT s.order_id, s.product_id, 'Parent order not found', SYSTIMESTAMP FROM io.thecodeforge.staging.raw_items s WHERE NOT EXISTS ( SELECT 1 FROM io.thecodeforge.orders o WHERE o.order_id = s.order_id ); v_orphan_count := SQL%ROWCOUNT; IF v_orphan_count > 0 THEN io.thecodeforge.logging.log_warning( 'FK_PRECHECK', v_orphan_count || ' orphaned items moved to rejected_items' ); END IF; -- Now insert only valid items INSERT INTO io.thecodeforge.order_items ( order_id, product_id, quantity, unit_price ) SELECT s.order_id, s.product_id, s.quantity, s.unit_price FROM io.thecodeforge.staging.raw_items s WHERE EXISTS ( SELECT 1 FROM io.thecodeforge.orders o WHERE o.order_id = s.order_id ); COMMIT; END load_order_items; / -- ORA-02292: integrity constraint violated - child record found -- Pattern: safe delete with dependency check CREATE OR REPLACE PROCEDURE io.thecodeforge.service.delete_customer( p_customer_id IN NUMBER ) IS v_child_count NUMBER; BEGIN SELECT COUNT(*) INTO v_child_count FROM io.thecodeforge.orders WHERE customer_id = p_customer_id; IF v_child_count > 0 THEN RAISE_APPLICATION_ERROR(-20010, 'Cannot delete customer ' || p_customer_id || ': ' || v_child_count || ' orders exist. ' || 'Archive orders first.'); END IF; DELETE FROM io.thecodeforge.customers WHERE customer_id = p_customer_id; END delete_customer; /
Query and Cursor Errors (ORA-01000 to ORA-01555)
Cursor management and query execution errors produce ORA-06512 stacks that point to OPEN, FETCH, or SELECT operations. These errors typically indicate resource exhaustion, cursor leaks, or concurrency issues rather than logic bugs.
ORA-01000 (maximum open cursors exceeded) means the session has hit the OPEN_CURSORS init parameter limit. This almost always indicates a cursor leak β cursors opened in a loop but never closed, or REF CURSORs returned to the caller without a close guarantee. The fix is never to increase OPEN_CURSORS blindly; find and plug the leak.
ORA-01555 (snapshot too old) is more insidious. It means Oracle's undo tablespace could not maintain a read-consistent image for the duration of your query or fetch loop. Long-running queries that started reading blocks hours ago find those undo records overwritten by newer transactions. The ORA-06512 stack will point to the FETCH statement where the read-consistency violation was detected, which may be thousands of iterations into a batch loop.
ORA-01422 (exact fetch returns more than requested number of rows) and ORA-01427 (single-row subquery returns more than one row) indicate that data assumptions have been violated β a query expected one row and got multiple. These are covered in detail under PL/SQL Logic Errors but also appear in cursor-based processing.
-- ORA-01000: maximum open cursors exceeded -- Root cause analysis query SELECT s.sid, s.serial#, s.username, s.program, COUNT(*) AS open_cursor_count FROM v$open_cursor oc JOIN v$session s ON oc.sid = s.sid GROUP BY s.sid, s.serial#, s.username, s.program HAVING COUNT(*) > 200 -- typical OPEN_CURSORS default is 300 ORDER BY open_cursor_count DESC; -- Leak-proof cursor pattern CREATE OR REPLACE PROCEDURE io.thecodeforge.etl.process_large_dataset IS CURSOR c_data IS SELECT /*+ PARALLEL(4) */ record_id, payload, created_at FROM io.thecodeforge.staging.raw_data WHERE status = 'PENDING' ORDER BY created_at; TYPE t_batch IS TABLE OF c_data%ROWTYPE; v_batch t_batch; v_limit PLS_INTEGER := 5000; v_total PLS_INTEGER := 0; BEGIN OPEN c_data; LOOP FETCH c_data BULK COLLECT INTO v_batch LIMIT v_limit; EXIT WHEN v_batch.COUNT = 0; FORALL i IN 1..v_batch.COUNT INSERT INTO io.thecodeforge.processed_data ( record_id, payload, processed_at ) VALUES ( v_batch(i).record_id, v_batch(i).payload, SYSTIMESTAMP ); -- Commit in batches to prevent ORA-01555 COMMIT; v_total := v_total + v_batch.COUNT; io.thecodeforge.logging.log_progress( 'PROCESS_LARGE_DATASET', v_total || ' rows processed' ); END LOOP; CLOSE c_data; io.thecodeforge.logging.log_info( 'PROCESS_LARGE_DATASET', 'Completed. Total rows: ' || v_total ); EXCEPTION WHEN OTHERS THEN IF c_data%ISOPEN THEN CLOSE c_data; END IF; io.thecodeforge.logging.log_error( 'PROCESS_LARGE_DATASET', DBMS_UTILITY.FORMAT_ERROR_STACK ); RAISE; END process_large_dataset; / -- ORA-01555 prevention: check undo retention and usage SELECT tablespace_name, ROUND(SUM(bytes) / 1024 / 1024) AS undo_mb, COUNT(*) AS extent_count FROM dba_undo_extents GROUP BY tablespace_name; SELECT name, value FROM v$parameter WHERE name IN ('undo_retention', 'undo_tablespace', 'undo_management');
PL/SQL Logic Errors (ORA-01403 to ORA-06550)
PL/SQL runtime errors β NO_DATA_FOUND, TOO_MANY_ROWS, VALUE_ERROR, ZERO_DIVIDE β are the most common application-level exceptions. They indicate logic bugs or violated assumptions about data state. The ORA-06512 stack trace is particularly valuable here because it pinpoints the exact SELECT INTO, assignment, or arithmetic operation that failed.
ORA-01403 (NO_DATA_FOUND) deserves special attention because it has a split personality. In a SELECT INTO context, it is a named exception. But inside a FETCH from an explicit cursor, it behaves differently β %NOTFOUND is set but the exception is not raised. This inconsistency trips up even experienced developers.
ORA-06502 (numeric or value error) covers a family of sub-errors: string buffer too small, number precision overflow, invalid character-to-number conversion. The SQLERRM message contains the sub-type, but many logging frameworks truncate it. Always log the full SQLERRM for ORA-06502 because the sub-type is the actual diagnosis.
ORA-06550 (compilation error) appears at runtime when dynamic SQL (EXECUTE IMMEDIATE) encounters a syntax or semantic error in the SQL string. The ORA-06512 line number will point to the EXECUTE IMMEDIATE statement, but the real problem is in the generated SQL text. Always log the SQL string alongside the error.
-- ORA-01403: NO_DATA_FOUND β robust lookup pattern CREATE OR REPLACE FUNCTION io.thecodeforge.service.get_customer_name( p_customer_id IN NUMBER ) RETURN VARCHAR2 IS v_name VARCHAR2(200); BEGIN SELECT customer_name INTO v_name FROM io.thecodeforge.customers WHERE customer_id = p_customer_id; RETURN v_name; EXCEPTION WHEN NO_DATA_FOUND THEN -- Decision point: is a missing customer a business error or expected? -- Option A: return NULL and let the caller decide RETURN NULL; -- Option B: raise with context (uncomment if missing customer is an error) -- RAISE_APPLICATION_ERROR(-20001, -- 'Customer not found: ' || p_customer_id); WHEN TOO_MANY_ROWS THEN -- This should never happen if customer_id is PK β indicates corruption io.thecodeforge.logging.log_error( 'DATA_INTEGRITY', 'Multiple rows for customer_id: ' || p_customer_id ); RAISE_APPLICATION_ERROR(-20002, 'Data integrity violation: multiple customers for ID ' || p_customer_id || '. Check unique constraint on CUSTOMERS.CUSTOMER_ID.'); END get_customer_name; / -- ORA-06502: VALUE_ERROR prevention with defensive sizing CREATE OR REPLACE PROCEDURE io.thecodeforge.etl.transform_address( p_raw_address IN VARCHAR2, p_city OUT VARCHAR2, -- target column is VARCHAR2(100) p_state OUT VARCHAR2 -- target column is VARCHAR2(50) ) IS v_delimiter_pos PLS_INTEGER; BEGIN v_delimiter_pos := INSTR(p_raw_address, ','); IF v_delimiter_pos = 0 THEN -- No delimiter β entire string is city, state is unknown p_city := SUBSTR(p_raw_address, 1, 100); -- defensive SUBSTR p_state := NULL; ELSE p_city := SUBSTR(TRIM(SUBSTR(p_raw_address, 1, v_delimiter_pos - 1)), 1, 100); p_state := SUBSTR(TRIM(SUBSTR(p_raw_address, v_delimiter_pos + 1)), 1, 50); END IF; IF LENGTH(p_raw_address) > 100 THEN io.thecodeforge.logging.log_warning( 'TRUNCATION', 'Address truncated from ' || LENGTH(p_raw_address) || ' to 100 chars. Original: ' || SUBSTR(p_raw_address, 1, 200) ); END IF; END transform_address; / -- ORA-06550: compilation error in dynamic SQL β log the SQL text CREATE OR REPLACE PROCEDURE io.thecodeforge.util.exec_dynamic( p_sql IN VARCHAR2 ) IS BEGIN EXECUTE IMMEDIATE p_sql; EXCEPTION WHEN OTHERS THEN io.thecodeforge.logging.log_error( 'DYNAMIC_SQL_FAILURE', 'SQLCODE: ' || SQLCODE || CHR(10) || 'SQL: ' || SUBSTR(p_sql, 1, 4000) || CHR(10) || 'Stack: ' || DBMS_UTILITY.FORMAT_ERROR_BACKTRACE ); RAISE; END exec_dynamic; / -- ORA-01476: ZERO_DIVIDE prevention CREATE OR REPLACE FUNCTION io.thecodeforge.util.safe_divide( p_numerator IN NUMBER, p_denominator IN NUMBER, p_default IN NUMBER DEFAULT 0 ) RETURN NUMBER DETERMINISTIC IS BEGIN IF p_denominator = 0 OR p_denominator IS NULL THEN RETURN p_default; END IF; RETURN p_numerator / p_denominator; END safe_divide; /
- Always handle NO_DATA_FOUND explicitly β decide whether missing data is an error or expected
- TOO_MANY_ROWS on a PK lookup indicates a missing or violated unique constraint β treat it as a data integrity alert
- Use BULK COLLECT with LIMIT instead of SELECT INTO when you cannot guarantee exactly one row
- For ORA-06502, always log the full SQLERRM β the sub-type (string buffer too small, numeric overflow, etc.) is the actual diagnosis
- For dynamic SQL errors (ORA-06550), always log the SQL text β the ORA-06512 line number only shows EXECUTE IMMEDIATE, which is useless without the generated SQL
- Use SUBSTR defensively on all string assignments where the source length is not guaranteed to fit the target variable
Memory and Resource Errors (ORA-04030 to ORA-04068)
Memory allocation failures and package state invalidation errors indicate infrastructure-level problems or schema change side effects. These are not logic bugs β they are environmental issues that require DBA or deployment-process intervention.
ORA-04030 (out of process memory) means the session's PGA allocation request exceeded available process memory. This is fatal to the session β there is no recovery within the same session because Oracle cannot allocate the memory needed to even run your exception handler reliably. The most common causes are: unconstrained BULK COLLECT without LIMIT (loading millions of rows into a PL/SQL collection), recursive CTEs that explode in cardinality, or hash joins on skewed data that exceed PGA_AGGREGATE_LIMIT.
ORA-04031 (unable to allocate shared memory) is the SGA equivalent β the shared pool, large pool, or Java pool is exhausted. This affects all sessions, not just the one that hit the limit. It often indicates hard parsing from non-bind-variable SQL or excessive SGA fragmentation.
ORA-04068 (existing state of packages has been discarded) is a deployment artifact. When a package body is recompiled (even with no changes to the spec), every session that has initialized that package's state (global variables, open cursors, initialized collections) gets ORA-04068 on the next call. The session must re-execute the call, which reinitializes the package. This is expected behavior during deployments but alarming if it appears outside maintenance windows.
-- ORA-04068 detection and automatic retry pattern CREATE OR REPLACE PROCEDURE io.thecodeforge.service.resilient_call IS v_retry_count PLS_INTEGER := 0; c_max_retries CONSTANT PLS_INTEGER := 3; e_package_state_discarded EXCEPTION; PRAGMA EXCEPTION_INIT(e_package_state_discarded, -4068); BEGIN LOOP BEGIN io.thecodeforge.core.process_batch; -- the real work EXIT; -- success β leave the retry loop EXCEPTION WHEN e_package_state_discarded THEN v_retry_count := v_retry_count + 1; IF v_retry_count <= c_max_retries THEN io.thecodeforge.logging.log_warning( 'ORA-04068', 'Package state discarded. Retry ' || v_retry_count || ' of ' || c_max_retries || '. Object: ' || $$PLSQL_UNIT ); -- Re-execution reinitializes the package state automatically ELSE io.thecodeforge.logging.log_error( 'ORA-04068', 'Max retries exhausted after ' || c_max_retries || ' attempts. Raising to caller.' ); RAISE; END IF; END; END LOOP; END resilient_call; / -- ORA-04030 prevention: monitor PGA memory usage SELECT s.sid, s.serial#, s.username, s.module, ROUND(p.pga_used_mem / 1024 / 1024, 1) AS pga_used_mb, ROUND(p.pga_alloc_mem / 1024 / 1024, 1) AS pga_alloc_mb, ROUND(p.pga_max_mem / 1024 / 1024, 1) AS pga_max_mb FROM v$session s JOIN v$process p ON s.paddr = p.addr WHERE p.pga_alloc_mem > 100 * 1024 * 1024 -- > 100 MB ORDER BY p.pga_alloc_mem DESC; -- ORA-04031 diagnosis: shared pool free memory and fragmentation SELECT pool, name, ROUND(bytes / 1024 / 1024, 1) AS mb FROM v$sgastat WHERE pool = 'shared pool' AND name IN ('free memory', 'library cache', 'sql area') ORDER BY bytes DESC; -- Check for hard parsing (non-bind-variable SQL) SELECT force_matching_signature, COUNT(*) AS versions, MIN(sql_text) AS sample_sql FROM v$sql GROUP BY force_matching_signature HAVING COUNT(*) > 50 ORDER BY versions DESC FETCH FIRST 10 ROWS ONLY;
Concurrency and Locking Errors (ORA-00054 to ORA-00060)
Concurrency errors produce ORA-06512 stacks in high-throughput transactional systems where multiple sessions compete for the same rows or resources. These errors are notoriously difficult to reproduce in development environments because they depend on timing, session ordering, and load patterns.
ORA-00054 (resource busy and acquire with NOWAIT) occurs when a DDL statement or a SELECT FOR UPDATE NOWAIT encounters a locked row. The ORA-06512 stack shows where the lock acquisition was attempted. This error is common in systems that mix DDL with DML β such as partition maintenance operations running concurrently with transactional inserts.
ORA-00060 (deadlock detected) means two or more sessions created a circular wait dependency on row-level locks. Oracle automatically detects the deadlock and rolls back the statement (not the transaction) in one of the sessions, raising ORA-00060 in that session. The ORA-06512 stack points to the DML statement that was chosen as the deadlock victim. Oracle also writes a deadlock trace file to the ADR home with the deadlock graph β this is essential for diagnosis.
ORA-00018 (maximum number of sessions exceeded) and ORA-00020 (maximum number of processes exceeded) indicate connection pool exhaustion or session leaks. They surface as ORA-06512 when PL/SQL code attempts to open a database link or use DBMS_SCHEDULER.
-- ORA-00060: deadlock retry pattern CREATE OR REPLACE PROCEDURE io.thecodeforge.service.update_inventory( p_product_id IN NUMBER, p_quantity IN NUMBER ) IS v_retry_count PLS_INTEGER := 0; c_max_retries CONSTANT PLS_INTEGER := 3; c_retry_delay CONSTANT NUMBER := 0.5; -- seconds e_deadlock EXCEPTION; PRAGMA EXCEPTION_INIT(e_deadlock, -60); BEGIN LOOP BEGIN UPDATE io.thecodeforge.inventory SET quantity_on_hand = quantity_on_hand - p_quantity, last_updated = SYSTIMESTAMP WHERE product_id = p_product_id AND quantity_on_hand >= p_quantity; IF SQL%ROWCOUNT = 0 THEN RAISE_APPLICATION_ERROR(-20003, 'Insufficient inventory for product ' || p_product_id || '. Requested: ' || p_quantity); END IF; EXIT; -- success EXCEPTION WHEN e_deadlock THEN v_retry_count := v_retry_count + 1; IF v_retry_count <= c_max_retries THEN io.thecodeforge.logging.log_warning( 'DEADLOCK', 'Deadlock on product ' || p_product_id || '. Retry ' || v_retry_count || ' of ' || c_max_retries ); DBMS_SESSION.SLEEP(c_retry_delay * v_retry_count); ELSE RAISE; END IF; END; END LOOP; END update_inventory; / -- ORA-00054: lock detection before DDL -- Check for active locks before attempting partition maintenance SELECT l.session_id, l.oracle_username, l.os_user_name, o.object_name, o.object_type, l.locked_mode, s.sql_id, s.status, s.last_call_et AS seconds_in_wait FROM dba_locks l JOIN dba_objects o ON l.object_id = o.object_id JOIN v$session s ON l.session_id = s.sid WHERE o.object_name = 'ORDERS' ORDER BY l.locked_mode DESC; -- Deadlock trace file location SELECT value AS trace_dir FROM v$diag_info WHERE name = 'Default Trace File';
Security and Privilege Errors (ORA-00942 to ORA-01031)
Privilege and security errors indicate authorization failures or schema visibility issues. These errors are deceptive because ORA-00942 (table or view does not exist) can mean either the object is genuinely missing or the caller lacks SELECT privilege β Oracle deliberately uses the same error code for both cases to avoid leaking schema information to unauthorized users.
ORA-01031 (insufficient privileges) is more straightforward β the session has authenticated but lacks the specific privilege needed for the requested operation. However, the nuance is in definer's rights vs. invoker's rights: in a definer's rights procedure (the default), role-based grants do not apply. Only direct grants to the procedure owner are effective. This catches teams that grant SELECT via a role and wonder why their packaged procedure still fails.
These errors spike after three common events: (1) schema reorganizations where objects move between schemas, (2) role-based privilege changes that break definer's rights procedures, and (3) deployments where new objects are created but grants are forgotten. A deployment checklist that includes grant verification prevents most production occurrences.
-- Distinguish missing object from missing privilege -- (Must be run by a DBA or user with SELECT on DBA_ views) CREATE OR REPLACE PROCEDURE io.thecodeforge.util.diagnose_942( p_object_name IN VARCHAR2, p_schema IN VARCHAR2 DEFAULT USER ) IS v_exists NUMBER; v_grant_count NUMBER; BEGIN -- Step 1: does the object exist at all? SELECT COUNT(*) INTO v_exists FROM dba_objects WHERE object_name = UPPER(p_object_name) AND owner = UPPER(p_schema) AND object_type IN ('TABLE','VIEW','SYNONYM','MATERIALIZED VIEW'); IF v_exists = 0 THEN DBMS_OUTPUT.PUT_LINE( 'DIAGNOSIS: Object ' || UPPER(p_schema) || '.' || UPPER(p_object_name) || ' does not exist.' ); -- Check for synonyms that might point to it SELECT COUNT(*) INTO v_exists FROM dba_synonyms WHERE synonym_name = UPPER(p_object_name); IF v_exists > 0 THEN DBMS_OUTPUT.PUT_LINE( 'NOTE: A synonym named ' || UPPER(p_object_name) || ' exists. Check if it points to the correct schema.' ); END IF; RETURN; END IF; -- Step 2: object exists β check direct grants SELECT COUNT(*) INTO v_grant_count FROM dba_tab_privs WHERE table_name = UPPER(p_object_name) AND owner = UPPER(p_schema) AND grantee = USER; DBMS_OUTPUT.PUT_LINE( 'Object exists. Direct grants to ' || USER || ': ' || v_grant_count ); -- Step 3: check role grants (won't work in definer's rights) FOR rec IN ( SELECT granted_role FROM dba_role_privs WHERE grantee = USER ) LOOP SELECT COUNT(*) INTO v_exists FROM dba_tab_privs WHERE table_name = UPPER(p_object_name) AND owner = UPPER(p_schema) AND grantee = rec.granted_role; IF v_exists > 0 THEN DBMS_OUTPUT.PUT_LINE( 'WARNING: Grant exists via role ' || rec.granted_role || ' β this will NOT work inside definer''s rights procedures' ); END IF; END LOOP; END diagnose_942; / -- Deployment grant verification script -- Run after every deployment to catch missing grants SELECT o.owner, o.object_name, o.object_type, 'MISSING GRANT' AS issue FROM dba_objects o WHERE o.owner = 'IO' AND o.object_type IN ('TABLE','VIEW') AND o.created > SYSDATE - 1 -- objects created in last 24 hours AND NOT EXISTS ( SELECT 1 FROM dba_tab_privs p WHERE p.owner = o.owner AND p.table_name = o.object_name AND p.grantee = 'APP_USER' -- your application schema ) ORDER BY o.created DESC;
- ORA-00942 can mean missing object OR missing privilege β Oracle hides the distinction intentionally for security
- Check DBA_OBJECTS first to confirm existence, then DBA_TAB_PRIVS for direct grants
- Role-based grants do NOT apply inside definer's rights procedures β use direct grants or switch to AUTHID CURRENT_USER
- Public synonyms can mask the real schema β verify the synonym target matches the expected schema
- After deployments, run a grant verification query to catch new objects missing application-level grants
- ORA-01031 during DDL often means the user has the privilege via a role but needs it directly β common with CREATE TABLE and CREATE VIEW
Type Conversion Errors (ORA-01722 to ORA-01861)
Type conversion errors are the silent killers of ETL pipelines and batch processing systems. They occur when Oracle cannot implicitly or explicitly convert a value from one datatype to another β typically string to number (ORA-01722) or string to date (ORA-01830, ORA-01858, ORA-01861). These errors produce ORA-06512 stacks that point to the INSERT, UPDATE, or comparison operation where the conversion was attempted.
The root cause is almost always dirty data from external sources: CSV files with currency symbols in numeric columns, dates in unexpected formats, or empty strings where NULLs were expected. The fix is never to make the conversion more permissive β it is to validate and cleanse input data before it reaches the DML layer.
ORA-01722 (invalid number) is particularly treacherous because it can lurk in WHERE clauses, not just in value assignments. If a VARCHAR2 column contains non-numeric values and you compare it to a NUMBER, Oracle's implicit conversion raises ORA-01722 on the rows that contain non-numeric data. This means the error may appear only when specific data rows are processed, making it non-reproducible with test data.
-- ORA-01722: invalid number β safe conversion pattern CREATE OR REPLACE FUNCTION io.thecodeforge.util.safe_to_number( p_value IN VARCHAR2, p_default IN NUMBER DEFAULT NULL ) RETURN NUMBER DETERMINISTIC IS BEGIN RETURN TO_NUMBER( REGEXP_REPLACE(p_value, '[^0-9.\-]', '') -- strip non-numeric chars ); EXCEPTION WHEN VALUE_ERROR THEN RETURN p_default; END safe_to_number; / -- ORA-01830/01858/01861: date conversion β defensive parsing CREATE OR REPLACE FUNCTION io.thecodeforge.util.safe_to_date( p_value IN VARCHAR2, p_format IN VARCHAR2 DEFAULT 'YYYY-MM-DD' ) RETURN DATE DETERMINISTIC IS v_date DATE; BEGIN -- Try the expected format first v_date := TO_DATE(p_value, p_format); RETURN v_date; EXCEPTION WHEN OTHERS THEN -- Try ISO 8601 fallback BEGIN v_date := TO_DATE(p_value, 'YYYY-MM-DD"T"HH24:MI:SS'); RETURN v_date; EXCEPTION WHEN OTHERS THEN -- Try US format fallback BEGIN v_date := TO_DATE(p_value, 'MM/DD/YYYY'); RETURN v_date; EXCEPTION WHEN OTHERS THEN io.thecodeforge.logging.log_warning( 'DATE_PARSE_FAILURE', 'Cannot parse: ' || SUBSTR(p_value, 1, 100) || ' with format ' || p_format ); RETURN NULL; END; END; END safe_to_date; / -- Pre-validation: find bad data before it hits your DML SELECT record_id, amount_text, CASE WHEN REGEXP_LIKE(amount_text, '^-?[0-9]+(\.[0-9]+)?$') THEN 'VALID' ELSE 'INVALID' END AS validation_status FROM io.thecodeforge.staging.raw_transactions WHERE NOT REGEXP_LIKE(NVL(amount_text,'X'), '^-?[0-9]+(\.[0-9]+)?$');
DML and MERGE Errors (ORA-30926 to ORA-38104)
MERGE statement errors are among the most confusing Oracle errors because the error messages are cryptic and the ORA-06512 stack points to the MERGE statement but does not indicate which row caused the failure.
ORA-30926 (unable to get a stable set of rows in the source tables) means the source query for a MERGE returned duplicate rows for the same join key. When Oracle attempts to both UPDATE and INSERT for the same key in a single MERGE execution, it cannot determine which action to take and raises this error. The fix is to deduplicate the source query β typically by adding ROW_NUMBER() OVER (PARTITION BY join_key ORDER BY preference_column) and filtering to rank 1.
ORA-38104 (columns referenced in the ON clause cannot be updated) is a syntactic restriction: you cannot UPDATE the columns used in the MERGE ON clause. If your merge key is also a business-updatable field, you must redesign the MERGE or use separate UPDATE and INSERT statements.
-- ORA-30926: fix by deduplicating the source -- BAD: source has duplicate customer_ids MERGE INTO io.thecodeforge.customers tgt USING io.thecodeforge.staging.customer_updates src ON (tgt.customer_id = src.customer_id) WHEN MATCHED THEN UPDATE SET tgt.email = src.email WHEN NOT MATCHED THEN INSERT (customer_id, email) VALUES (src.customer_id, src.email); -- Fails with ORA-30926 if staging has duplicate customer_ids -- GOOD: deduplicated source MERGE INTO io.thecodeforge.customers tgt USING ( SELECT customer_id, email FROM ( SELECT customer_id, email, ROW_NUMBER() OVER ( PARTITION BY customer_id ORDER BY updated_at DESC -- keep most recent ) AS rn FROM io.thecodeforge.staging.customer_updates ) WHERE rn = 1 ) src ON (tgt.customer_id = src.customer_id) WHEN MATCHED THEN UPDATE SET tgt.email = src.email WHEN NOT MATCHED THEN INSERT (customer_id, email) VALUES (src.customer_id, src.email); -- Pre-check for duplicates before running MERGE SELECT customer_id, COUNT(*) AS dup_count FROM io.thecodeforge.staging.customer_updates GROUP BY customer_id HAVING COUNT(*) > 1 ORDER BY dup_count DESC;
- ORA-30926 always means duplicate join keys in the source β deduplicate with
ROW_NUMBER()before MERGE - Add a pre-MERGE duplicate check to your ETL pipelines β fail fast with a clear message instead of cryptic ORA-30926
- ORA-38104 is a design constraint β you cannot UPDATE the ON clause columns; restructure the MERGE or use separate DML
- For complex MERGE operations, consider splitting into separate INSERT and UPDATE statements for clarity and debuggability
- Test MERGE statements with production-volume data β ORA-30926 only appears when duplicates exist, which test data often lacks
| Error Code | Error Name | Category | Severity | Frequency in Production | Detailed Guide |
|---|---|---|---|---|---|
| ORA-00001 | unique constraint violated | Data Integrity | High | Very Common | Constraint Violation Guide |
| ORA-00018 | maximum number of sessions exceeded | Resource | Critical | Common | Session Management Guide |
| ORA-00020 | maximum number of processes exceeded | Resource | Critical | Occasional | Process Limit Guide |
| ORA-00054 | resource busy and acquire with NOWAIT | Concurrency | Medium | Common | Lock Management Guide |
| ORA-00060 | deadlock detected while waiting for resource | Concurrency | High | Occasional | Deadlock Resolution Guide |
| ORA-00600 | internal error code | Internal | Critical | Rare | Internal Error Triage Guide |
| ORA-00904 | invalid identifier | Syntax | Medium | Common | Schema Reference Guide |
| ORA-00942 | table or view does not exist | Security/Schema | High | Common | Object Access Guide |
| ORA-01000 | maximum open cursors exceeded | Cursor | High | Common | Cursor Management Guide |
| ORA-01012 | not logged on | Connection | Critical | Occasional | Connection Handling Guide |
| ORA-01017 | invalid username/password; logon denied | Security | High | Common | Authentication Guide |
| ORA-01031 | insufficient privileges | Security | High | Common | Privilege Management Guide |
| ORA-01400 | cannot insert NULL | Data Integrity | Medium | Very Common | NULL Handling Guide |
| ORA-01403 | no data found | Logic | Medium | Very Common | SELECT INTO Guide |
| ORA-01422 | exact fetch returns more than requested number of rows | Logic | High | Common | Row Count Guide |
| ORA-01427 | single-row subquery returns more than one row | Query | High | Common | Subquery Guide |
| ORA-01476 | divisor is equal to zero | Arithmetic | Medium | Occasional | Arithmetic Safety Guide |
| ORA-01555 | snapshot too old | Transaction | Critical | Occasional | Undo Management Guide |
| ORA-01722 | invalid number | Conversion | High | Very Common | Type Conversion Guide |
| ORA-01830 | date format picture ends before converting entire input string | Conversion | Medium | Common | Date Format Guide |
| ORA-01843 | not a valid month | Conversion | Medium | Common | Date Validation Guide |
| ORA-01858 | a non-numeric character was found where a numeric was expected | Conversion | Medium | Common | Date Parsing Guide |
| ORA-01861 | literal does not match format string | Conversion | Medium | Common | Format Mask Guide |
| ORA-02049 | timeout: distributed transaction waiting for lock | Concurrency | High | Occasional | Distributed Lock Guide |
| ORA-02291 | integrity constraint violated - parent key not found | Data Integrity | High | Common | FK Violation Guide |
| ORA-02292 | integrity constraint violated - child record found | Data Integrity | High | Common | Delete Constraint Guide |
| ORA-04030 | out of process memory | Memory | Critical | Occasional | Memory Management Guide |
| ORA-04031 | unable to allocate shared memory | Memory | Critical | Occasional | Shared Pool Guide |
| ORA-04068 | existing state of packages has been discarded | Package State | High | Occasional | Package State Guide |
| ORA-06502 | PL/SQL: numeric or value error | Conversion | High | Very Common | Value Error Guide |
| ORA-06504 | PL/SQL: return types of result set variables or query do not match | Logic | Medium | Occasional | REF CURSOR Guide |
| ORA-06511 | PL/SQL: cursor already open | Cursor | Medium | Occasional | Cursor Lifecycle Guide |
| ORA-06531 | PL/SQL: collection is not initialized | Collection | Medium | Common | Collection Guide |
| ORA-06533 | PL/SQL: subscript beyond count | Collection | Medium | Common | Index Bounds Guide |
| ORA-06550 | PL/SQL: compilation error | Compilation | High | Common | Compilation Guide |
| ORA-12899 | value too large for column | Data Integrity | Medium | Very Common | Column Sizing Guide |
| ORA-12170 | TNS: connect timeout occurred | Connection | High | Occasional | Network Timeout Guide |
| ORA-20001 | application error (custom) | Application | Varies | Common | Custom Error Guide |
| ORA-20999 | application error (custom range) | Application | Varies | Common | Custom Error Guide |
| ORA-30926 | unable to get a stable set of rows in the source tables | DML | High | Occasional | Merge Stability Guide |
| ORA-38104 | columns referenced in the ON clause cannot be updated | DML | Medium | Common | Merge Syntax Guide |
π― Key Takeaways
- ORA-06512 is a stack trace indicator β never the actual error to fix; always parse the preceding ORA code
- Always parse the first non-06512 ORA code from the error stack for alerting and triage routing
- 30+ common Oracle errors trigger ORA-06512 β categorize by type (data integrity, cursor, logic, memory, security, conversion, concurrency, DML) for efficient triage
- Log the full error stack using FORMAT_ERROR_STACK, FORMAT_ERROR_BACKTRACE, and FORMAT_CALL_STACK alongside session context, object name, and business identifiers
- Build automated stack parsers at the monitoring layer β never rely on manual reading for production triage
- Audit WHEN OTHERS THEN NULL handlers quarterly β they are silent data corruption vectors that hide the errors ORA-06512 would otherwise reveal
- Use DEFAULT ON CONVERSION ERROR (12c+), LOG ERRORS INTO, and SAVE EXCEPTIONS to handle errors at the row level instead of aborting entire batches
β Common Mistakes to Avoid
Interview Questions on This Topic
- QHow would you build a production monitoring system that correctly handles ORA-06512 error stacks?SeniorReveal
- QWhat is the difference between ORA-06512 and ORA-06511, and when does each occur?Mid-levelReveal
- QHow do you distinguish ORA-00942 (table does not exist) caused by a missing object from one caused by a missing privilege?Mid-levelReveal
- QWhat is the best practice for handling ORA-04068 (package state discarded) in production with zero-downtime deployments?SeniorReveal
- QExplain why DBMS_UTILITY.FORMAT_ERROR_BACKTRACE is preferable to FORMAT_ERROR_STACK for logging ORA-06512 information, and when each is appropriate.SeniorReveal
Frequently Asked Questions
Can ORA-06512 appear without a preceding error code?
No. ORA-06512 always accompanies another error β it is structurally impossible for Oracle to generate a stack trace frame without an originating error. If you see only ORA-06512 in your logs, the preceding error was truncated by log line length limits, a truncating logger, or SQLERRM's 512-byte limit. Check the full trace file in the ADR home (v$diag_info WHERE name = 'Default Trace File') for the complete error stack. Also check whether your logging framework truncates SQLERRM β switch to DBMS_UTILITY.FORMAT_ERROR_STACK which returns up to 2000 bytes.
How do I find the line number in the source code from ORA-06512?
ORA-06512 includes the line number in the format: ORA-06512: at "OWNER.OBJECT_NAME", line N. Query DBA_SOURCE: SELECT line, text FROM dba_source WHERE owner = 'OWNER' AND name = 'OBJECT_NAME' AND line BETWEEN N-2 AND N+2 ORDER BY line. This shows the exact statement with surrounding context. If the package was recompiled after the error, the line numbers may have shifted β check your version control at the deployment timestamp. In 19c+, DBMS_UTILITY.FORMAT_ERROR_BACKTRACE preserves the original line number even through re-raises.
Why do I see multiple ORA-06512 lines in a single error stack?
Each ORA-06512 represents one procedure boundary where the exception propagated without being caught. If procedure A calls B, B calls C, and C raises an error, you see three ORA-06512 lines: one for C (where the error originated), one for B (where it propagated), and one for A (the entry point). The actual error appears above all ORA-06512 lines. The deepest procedure in the call chain is listed first (closest to the error), and the outermost caller is listed last. If you see 10+ frames, consider adding exception handlers at intermediate layers to provide business context before re-raising.
Should I catch ORA-06512 specifically in my exception handlers?
No. ORA-06512 is never raised directly as an exception β it is metadata appended to the error stack by the PL/SQL runtime as exceptions propagate. You cannot and should not catch it. Catch the actual error code (ORA-01403, ORA-00001, etc.) using named exceptions (NO_DATA_FOUND, DUP_VAL_ON_INDEX) or SQLCODE checks in WHEN OTHERS. Use WHEN OTHERS to capture the full stack for logging via DBMS_UTILITY.FORMAT_ERROR_STACK and FORMAT_ERROR_BACKTRACE, then always re-raise.
How do I reduce ORA-06512 noise in my alert logs?
Configure your log parser to extract the primary error code from the stack (the first ORA- code that is not ORA-06512) and alert only on that code. Suppress ORA-06512 from alert rules entirely β it is informational, not actionable. Build dashboards that group errors by primary_error_code, not by ORA-06512 occurrence. Use the error_log table with a parsed primary_error_code column for trend analysis. Set up error rate baselines per error code and alert only when rates exceed the baseline by a configurable threshold (e.g., 2x the 7-day rolling average).
What is the difference between SQLERRM, FORMAT_ERROR_STACK, and FORMAT_ERROR_BACKTRACE?
SQLERRM returns the error message for the current SQLCODE, limited to 512 bytes β it truncates deep stacks. FORMAT_ERROR_STACK returns the full error stack up to 2000 bytes, including all ORA codes and messages. FORMAT_ERROR_BACKTRACE returns the line-level trace showing exactly where each error originated, preserved through re-raises β this is the one that gives you the original line number even after the exception has propagated through multiple handlers. Best practice: log all three. Use FORMAT_ERROR_BACKTRACE for debugging line numbers, FORMAT_ERROR_STACK for error codes and messages, and FORMAT_CALL_STACK for the procedure call chain.
Does ORA-06512 affect performance?
ORA-06512 itself is just a string appended to the error stack β the overhead is negligible for occasional exceptions. However, in systems that raise exceptions as flow control (e.g., using NO_DATA_FOUND in tight loops instead of COUNT(*) pre-checks), the cumulative overhead of building and formatting stack traces adds 5β15 ms per propagation layer per exception. In a loop processing 100,000 rows with a 10% NO_DATA_FOUND rate and a 5-layer call chain, that is 10,000 Γ 5 Γ 10 ms = 500 seconds of pure exception overhead. Use exceptions for exceptional conditions, not control flow.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.