Skip to content
Homeβ€Ί Databaseβ€Ί Complete List of Common Oracle Errors That Cause ORA-06512

Complete List of Common Oracle Errors That Cause ORA-06512

Where developers are forged. Β· Structured learning Β· Free forever.
πŸ“ Part of: PL/SQL β†’ Topic 20 of 27
Curated table of 30+ errors that end with ORA-06512.
βš™οΈ Intermediate β€” basic Database knowledge assumed
In this tutorial, you'll learn
Curated table of 30+ errors that end with 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
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
⚑Quick Answer
  • 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
🚨 START HERE
ORA-06512 Stack Parsing Cheat Sheet
Commands to extract the real error from ORA-06512 stacks β€” copy-paste ready for production triage
🟑Need to find the primary error in a stack trace
Immediate ActionExtract the first ORA- code that is not ORA-06512 from the error string
Commands
-- 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;
Fix NowParse SQLERRM with REGEXP_SUBSTR for the first ORA- code that is not 06512. If the first match IS 06512, grab the second match. Pipe this parsed code into your alert routing.
🟑Need to identify which procedure raises the most ORA-06512 stacks
Immediate ActionQuery the error log table grouped by source object to find hot spots
Commands
-- 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;
Fix NowRecompile invalid objects with ALTER PACKAGE owner.package_name COMPILE BODY; and add explicit exception handling at the identified hot-spot lines. Use DBMS_UTILITY.COMPILE_SCHEMA for bulk recompilation.
🟑Need to correlate ORA-06512 with session activity at the time of the error
Immediate ActionJoin error timestamps against ASH data to identify the SQL and wait events
Commands
-- 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;
Fix NowUse the SQL ID from ASH to pull the execution plan and identify whether the error correlates with a specific plan change, lock wait, or resource bottleneck.
Production IncidentAlert Storm from Misconfigured ORA-06512 MonitoringA monitoring rule alerting on every ORA-06512 generated 14,000 alerts overnight, drowning the on-call engineer in noise while a real data corruption issue went undetected for six hours.
SymptomPagerDuty flooded with ORA-06512 alerts starting at 01:17 UTC. Alert fatigue caused the team to mute the channel entirely. An ORA-00001 unique constraint violation was silently corrupting order reconciliation data by allowing duplicate settlement records to bypass the dedup check via a WHEN OTHERS THEN NULL handler. Six hours and roughly 42,000 duplicate rows later, the finance team reported mismatched totals.
AssumptionThe monitoring team configured alerts on ORA-06512 because it appeared in over 90% of error log entries. They assumed that high frequency meant high severity. No one questioned whether ORA-06512 was the cause or a symptom.
Root causeORA-06512 is a stack trace indicator, not an error. It accompanies every PL/SQL exception that propagates across a procedure boundary. Alerting on it produces noise proportional to exception volume β€” including benign NO_DATA_FOUND exceptions in lookup functions. The real error (ORA-00001) was buried in the stack and never surfaced as a distinct alert because the alert rule matched on ORA-06512 first and stopped parsing. Additionally, the offending procedure had a WHEN OTHERS THEN NULL handler that swallowed the constraint violation, so the stack was only visible in trace files, not in the application error log.
Fix1. Removed ORA-06512 from all alert rules immediately 2. Added alert rules for specific high-severity error codes: ORA-00001, ORA-01555, ORA-04030, ORA-04031, ORA-00060, ORA-00600 3. Implemented an error stack parsing function (io.thecodeforge.util.parse_primary_error) that extracts the first non-06512 ORA code from SQLERRM 4. Added an error_log table with a parsed primary_error_code column for trend analysis and dashboarding 5. Audited all WHEN OTHERS handlers across 340+ packages β€” removed 47 instances of WHEN OTHERS THEN NULL 6. Deployed a post-incident reconciliation script to identify and merge the 42,000 duplicate settlement records
Key Lesson
Never alert on ORA-06512 alone β€” always parse the preceding error code from the full stackAlert on the root error, not the stack trace indicatorBuild error stack parsers for production monitoring systems and validate them against real trace filesAudit WHEN OTHERS THEN NULL handlers quarterly β€” they are silent data corruption vectorsRun reconciliation checks on critical data pipelines independently of application-level error logging
Production Debug GuideSymptom-to-action mapping for stack trace debugging in production environments
ORA-06512 appears in the alert log with no preceding error visible→Check trace files in BACKGROUND_DUMP_DEST (or the ADR home in 11g+) for the full error stack. The preceding error is always present in the same trace entry — it may have been truncated by the alert log's line limit. Run: SELECT value FROM v$diag_info WHERE name = 'Default Trace File'; then grep for the timestamp.
Multiple ORA-06512 lines in a single error stack→Read bottom-up: the lowest ORA-06512 is the outermost caller (the entry point). The actual error is above the first ORA-06512 line. Each intermediate ORA-06512 represents one procedure boundary where the exception propagated without being caught. If the stack is deeper than 10 frames, investigate whether the call chain can be flattened.
Application catches ORA-06512 but not the real error→Modify the exception handler to capture the full SQLERRM string and DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, not just SQLCODE. Parse for the first error code in the string using REGEXP_SUBSTR. Note that SQLERRM truncates at 512 bytes — for deeper stacks use FORMAT_ERROR_STACK which returns up to 2000 bytes.
ORA-06512 with line number 0 or no line number→Error occurred in an anonymous PL/SQL block, dynamically executed code (EXECUTE IMMEDIATE), or a trigger where source line tracking is unavailable. Check DBMS_OUTPUT, application logs, or v$sql for the SQL text. If the error is in dynamic SQL, wrap the EXECUTE IMMEDIATE in a BEGIN/EXCEPTION block that logs the SQL string before re-raising.
ORA-06512 stack references a procedure that has been recompiled since the error→Line numbers in ORA-06512 correspond to the source at the time of execution. If the package has been recompiled, query DBA_SOURCE_HISTORY (if available) or your version control system at the deployment timestamp. In 19c+, use DBMS_UTILITY.FORMAT_ERROR_BACKTRACE which includes the object edition.
ORA-06512 appears intermittently under high concurrency→The underlying error is likely concurrency-related: ORA-00060 (deadlock), ORA-00054 (resource busy), or ORA-01555 (snapshot too old). Correlate the timestamp with AWR snapshots (DBA_HIST_ACTIVE_SESS_HISTORY) to identify the blocking session or undo pressure at the time of the error.

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

stack_trace_parsing.sql Β· SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566
-- 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;
/
Mental Model
Stack Trace Reading Order
Read error stacks bottom-up like a function call chain β€” the deepest call is at the bottom, and the real error sits above all ORA-06512 lines.
  • 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
πŸ“Š Production Insight
Stack trace parsing must happen at the monitoring layer, not in the developer's head at 3 AM.
Do not rely on humans reading raw stacks manually during incidents.
Automate primary error extraction for alert routing and build dashboards grouped by primary_error_code.
In Oracle 12.2+, UTL_CALL_STACK provides programmatic access to error depth, error code at each frame, and backtrace unit/line β€” use it for structured logging instead of string parsing when available.
🎯 Key Takeaway
ORA-06512 is the stack frame, not the error.
Always extract the preceding error code for triage.
Automate parsing β€” never alert on ORA-06512 alone.
Use FORMAT_ERROR_BACKTRACE (not FORMAT_ERROR_STACK) to preserve original line numbers after re-raising.
Stack Trace Analysis Strategy
IfSingle ORA-06512 in stack
β†’
UseError occurred directly in the called procedure β€” check the exact line number in DBA_SOURCE and inspect the statement
IfMultiple ORA-06512 lines (2–5 frames)
β†’
UseNormal call chain depth β€” trace from the first ORA-06512 (deepest) upward to understand the propagation path
IfDeep ORA-06512 stack (6+ frames)
β†’
UseConsider adding exception handlers at intermediate layers to provide context and break the propagation chain β€” but always re-raise
IfORA-06512 at line 0 or no line number
β†’
UseError originated in an anonymous block, EXECUTE IMMEDIATE, or a trigger β€” check application logs and v$sql for the dynamic SQL text
IfNo ORA-06512 visible at all
β†’
UseError was raised at the top level SQL layer β€” no PL/SQL call stack involved; check v$sql and the session's current statement

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.

data_integrity_errors.sql Β· SQL
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
-- 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;
/
⚠ Constraint Error Handling in Production
πŸ“Š Production Insight
Constraint violations spike during batch loads and data migrations.
Pre-validate referential integrity before bulk inserts to avoid row-by-row rollbacks.
Use SAVEPOINT to isolate violating records in batches β€” a single bad row should not abort 500,000 good ones.
Monitor constraint violation rates as a data quality metric: a sudden spike means something changed upstream.
In Oracle 12c+, use LOG ERRORS INTO for DML error logging β€” it captures rejected rows automatically without procedural exception handling.
🎯 Key Takeaway
Data integrity errors are the number-one source of ORA-06512 in transactional systems.
Always log constraint names and offending key values.
Pre-validate FK relationships in batch processing to avoid cascading failures.
Use LOG ERRORS INTO for bulk DML when row-level exception handling is impractical.

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.

cursor_errors.sql Β· SQL
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
-- 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');
⚠ Cursor Leak Prevention Checklist
πŸ“Š Production Insight
Cursor leaks accumulate silently across sessions and only crash when OPEN_CURSORS is exhausted β€” which might be days after deployment.
Monitor v$open_cursor weekly and alert when any session exceeds 70% of the OPEN_CURSORS limit.
ORA-01555 kills long-running queries without warning and without a retry mechanism β€” the entire fetch loop is lost.
For mission-critical batch jobs, use BULK COLLECT with LIMIT and COMMIT after each batch. This trades perfect read-consistency for reliability.
In Oracle 19c+, consider using DBMS_PARALLEL_EXECUTE to chunk large table processing β€” it avoids both ORA-01555 and long-running transaction issues.
🎯 Key Takeaway
Cursor errors indicate resource exhaustion or long-transaction conflicts.
Always close cursors in exception handlers β€” leaked cursors are invisible until they crash the session.
Commit periodically in long-running batch operations to prevent ORA-01555.
Use BULK COLLECT with LIMIT as the default pattern for any loop processing more than a few hundred rows.

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.

logic_errors.sql Β· SQL
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
-- 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;
/
πŸ’‘SELECT INTO Best Practices
  • 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
πŸ“Š Production Insight
SELECT INTO failures are the top source of NO_DATA_FOUND in production. Add row-existence checks before SELECT INTO in critical paths β€” or use a cursor-based approach that handles zero rows gracefully.
Log the query parameters alongside the exception for debugging. An error message saying 'no data found' without the customer_id that was searched is useless at 3 AM.
ORA-06502 with sub-type 'character string buffer too small' is the third most common PL/SQL error in ETL systems β€” use SUBSTR defensively on every assignment from external data.
For ORA-06550 in dynamic SQL, consider using DBMS_SQL.PARSE with error handling instead of EXECUTE IMMEDIATE for complex generated SQL β€” it gives you more control over error reporting.
🎯 Key Takeaway
Logic errors reveal violated assumptions about data state.
Always handle SELECT INTO exceptions explicitly and log the search parameters.
Use SUBSTR defensively on all string assignments from untrusted sources.
For dynamic SQL, log the SQL text β€” the line number is meaningless without it.

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.

resource_errors.sql Β· SQL
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
-- 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;
⚠ Resource Error Impact Assessment
πŸ“Š Production Insight
ORA-04030 crashes are unrecoverable without session restart β€” and if the session held locks, those locks are held until PMON cleans up, which can take minutes.
Set PGA_AGGREGATE_LIMIT (available in 12c+) to a sane value like 2x PGA_AGGREGATE_TARGET. This kills runaway sessions proactively instead of letting them destabilize the instance.
ORA-04068 spikes immediately after deployments that recompile packages. If you deploy with zero-downtime patterns, implement the retry wrapper shown above in every entry-point procedure.
For ORA-04031, the long-term fix is always to use bind variables. CURSOR_SHARING=FORCE is a band-aid, not a solution β€” it has its own side effects including suboptimal execution plans.
Monitor v$pgastat and v$process memory metrics in your APM tool with 70% threshold alerts.
🎯 Key Takeaway
Memory errors require infrastructure intervention, not code fixes.
Implement retry logic for ORA-04068 in all deployment-sensitive call paths.
Monitor PGA per session and SGA shared pool to predict crashes before they occur.
Set PGA_AGGREGATE_LIMIT to prevent runaway memory consumption.

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.

concurrency_errors.sql Β· SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960
-- 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';
⚠ Concurrency Error Response Playbook
πŸ“Š Production Insight
Deadlocks in production often indicate a missing index on a foreign key column β€” unindexed FKs cause full table locks during parent-row updates or deletes.
Check every FK column for a supporting index: SELECT * FROM dba_constraints WHERE constraint_type = 'R' and cross-reference with dba_ind_columns.
Connection pool exhaustion (ORA-00018/ORA-00020) is almost always caused by session leaks in application code β€” connections opened but never returned to the pool. Monitor v$session grouped by program and machine to find the leaking application.
For high-contention tables, consider using SELECT FOR UPDATE SKIP LOCKED (12c+) to process only unlocked rows β€” this eliminates both deadlocks and lock waits at the cost of non-deterministic processing order.
🎯 Key Takeaway
Concurrency errors require retry logic with backoff, not just error handling.
Deadlocks roll back only the statement β€” check transaction consistency after ORA-00060.
Index all FK columns to prevent unnecessary table-level locks.
Establish consistent lock acquisition order across all procedures that modify shared tables.

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.

security_errors.sql Β· SQL
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
-- 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;
πŸ”₯Privilege Error Diagnosis Checklist
  • 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
πŸ“Š Production Insight
Privilege errors spike after three events: schema reorganizations, role changes, and deployments with missing grants.
Definer's rights procedures (the default) ignore role grants β€” this is the single most common root cause of 'it works in SQL*Plus but fails in the procedure.'
Build a post-deployment grant checker into your CI/CD pipeline: compare new objects against expected grantee lists and fail the deployment if grants are missing.
For cross-schema access in microservice-style database designs, consider using AUTHID CURRENT_USER with explicit schema prefixes β€” this makes privilege requirements transparent.
Audit privilege changes with DBA_AUDIT_TRAIL or Unified Auditing to trace when a grant was revoked.
🎯 Key Takeaway
ORA-00942 has two causes β€” distinguish them by checking DBA_OBJECTS before DBA_TAB_PRIVS.
Role grants do not apply in definer's rights procedures β€” this is the most common privilege confusion.
Automate grant verification in your deployment pipeline to prevent production privilege errors.

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.

conversion_errors.sql Β· SQL
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
-- 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]+)?$');
⚠ Conversion Error Prevention
πŸ“Š Production Insight
Conversion errors are data quality indicators, not code bugs.
Track conversion failure rates as a data quality metric β€” a sudden spike means the upstream data source changed its format.
In Oracle 12c+, use the DEFAULT ON CONVERSION ERROR clause for inline safe conversion: TO_NUMBER(val DEFAULT 0 ON CONVERSION ERROR). This eliminates the need for wrapper functions in SQL.
For ETL pipelines, always load external data as VARCHAR2 into a staging table first. Validate and convert during the transform phase. Never INSERT directly into typed columns from external sources.
🎯 Key Takeaway
Type conversion errors are data quality problems, not code bugs.
Always use explicit conversion with format masks β€” never rely on implicit conversion.
Pre-validate staging data before it reaches DML operations.
Use DEFAULT ON CONVERSION ERROR in 12c+ for inline safe conversion.

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.

merge_errors.sql Β· SQL
1234567891011121314151617181920212223242526272829303132333435
-- 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;
πŸ’‘MERGE Error Prevention
  • 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
πŸ“Š Production Insight
ORA-30926 is the top MERGE error in ETL systems and is almost always caused by a source feed that contains duplicate records for the same business key.
Build a duplicate detection step into every ETL pipeline that uses MERGE. Log the duplicate counts and flag them for upstream data quality review.
In high-volume systems, consider using PL/SQL FORALL with SAVE EXCEPTIONS instead of MERGE β€” it gives you per-row error handling that MERGE cannot provide.
🎯 Key Takeaway
ORA-30926 means duplicate join keys in the MERGE source β€” always deduplicate before MERGE.
Add pre-MERGE duplicate checks to ETL pipelines.
For complex merge logic, consider separate INSERT/UPDATE statements for better error isolation.
πŸ—‚ 30+ Oracle Errors That Trigger ORA-06512
Complete reference table organized by category with severity, production frequency, and guide links
Error CodeError NameCategorySeverityFrequency in ProductionDetailed Guide
ORA-00001unique constraint violatedData IntegrityHighVery CommonConstraint Violation Guide
ORA-00018maximum number of sessions exceededResourceCriticalCommonSession Management Guide
ORA-00020maximum number of processes exceededResourceCriticalOccasionalProcess Limit Guide
ORA-00054resource busy and acquire with NOWAITConcurrencyMediumCommonLock Management Guide
ORA-00060deadlock detected while waiting for resourceConcurrencyHighOccasionalDeadlock Resolution Guide
ORA-00600internal error codeInternalCriticalRareInternal Error Triage Guide
ORA-00904invalid identifierSyntaxMediumCommonSchema Reference Guide
ORA-00942table or view does not existSecurity/SchemaHighCommonObject Access Guide
ORA-01000maximum open cursors exceededCursorHighCommonCursor Management Guide
ORA-01012not logged onConnectionCriticalOccasionalConnection Handling Guide
ORA-01017invalid username/password; logon deniedSecurityHighCommonAuthentication Guide
ORA-01031insufficient privilegesSecurityHighCommonPrivilege Management Guide
ORA-01400cannot insert NULLData IntegrityMediumVery CommonNULL Handling Guide
ORA-01403no data foundLogicMediumVery CommonSELECT INTO Guide
ORA-01422exact fetch returns more than requested number of rowsLogicHighCommonRow Count Guide
ORA-01427single-row subquery returns more than one rowQueryHighCommonSubquery Guide
ORA-01476divisor is equal to zeroArithmeticMediumOccasionalArithmetic Safety Guide
ORA-01555snapshot too oldTransactionCriticalOccasionalUndo Management Guide
ORA-01722invalid numberConversionHighVery CommonType Conversion Guide
ORA-01830date format picture ends before converting entire input stringConversionMediumCommonDate Format Guide
ORA-01843not a valid monthConversionMediumCommonDate Validation Guide
ORA-01858a non-numeric character was found where a numeric was expectedConversionMediumCommonDate Parsing Guide
ORA-01861literal does not match format stringConversionMediumCommonFormat Mask Guide
ORA-02049timeout: distributed transaction waiting for lockConcurrencyHighOccasionalDistributed Lock Guide
ORA-02291integrity constraint violated - parent key not foundData IntegrityHighCommonFK Violation Guide
ORA-02292integrity constraint violated - child record foundData IntegrityHighCommonDelete Constraint Guide
ORA-04030out of process memoryMemoryCriticalOccasionalMemory Management Guide
ORA-04031unable to allocate shared memoryMemoryCriticalOccasionalShared Pool Guide
ORA-04068existing state of packages has been discardedPackage StateHighOccasionalPackage State Guide
ORA-06502PL/SQL: numeric or value errorConversionHighVery CommonValue Error Guide
ORA-06504PL/SQL: return types of result set variables or query do not matchLogicMediumOccasionalREF CURSOR Guide
ORA-06511PL/SQL: cursor already openCursorMediumOccasionalCursor Lifecycle Guide
ORA-06531PL/SQL: collection is not initializedCollectionMediumCommonCollection Guide
ORA-06533PL/SQL: subscript beyond countCollectionMediumCommonIndex Bounds Guide
ORA-06550PL/SQL: compilation errorCompilationHighCommonCompilation Guide
ORA-12899value too large for columnData IntegrityMediumVery CommonColumn Sizing Guide
ORA-12170TNS: connect timeout occurredConnectionHighOccasionalNetwork Timeout Guide
ORA-20001application error (custom)ApplicationVariesCommonCustom Error Guide
ORA-20999application error (custom range)ApplicationVariesCommonCustom Error Guide
ORA-30926unable to get a stable set of rows in the source tablesDMLHighOccasionalMerge Stability Guide
ORA-38104columns referenced in the ON clause cannot be updatedDMLMediumCommonMerge 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

    βœ•Alerting on ORA-06512 as the primary error
    Symptom

    Alert storm with thousands of ORA-06512 notifications. Real errors buried in noise. Alert fatigue causes on-call engineers to mute channels and miss critical issues like data corruption or deadlocks.

    Fix

    Parse the full error stack and alert on the first non-06512 ORA code. Use io.thecodeforge.util.parse_primary_error to extract the real error code. Remove ORA-06512 from all alert rules. Build dashboards grouped by primary_error_code.

    βœ•Reading error stack top-down instead of bottom-up
    Symptom

    Developers fix the wrong procedure β€” they start debugging the caller (bottom of stack) instead of the procedure where the error actually occurred. Hours wasted investigating code that simply propagated the exception.

    Fix

    Read error stacks bottom-up: the deepest ORA-06512 is the outermost caller (the entry point). The actual error is always above the first ORA-06512 line. The first ORA-06512 after the error code is the procedure and line where the exception was raised.

    βœ•Not logging the full error stack including backtrace
    Symptom

    Error log shows only SQLCODE -1403 with no context. No information about which procedure, which parameters, which call chain, or which line triggered the error. Reproduction requires guessing.

    Fix

    Always log DBMS_UTILITY.FORMAT_ERROR_STACK (full error text up to 2000 bytes), DBMS_UTILITY.FORMAT_ERROR_BACKTRACE (line-level trace preserved through re-raises), DBMS_UTILITY.FORMAT_CALL_STACK (who called whom), session ID, timestamp, and $$PLSQL_UNIT/$$PLSQL_LINE for the handler location.

    βœ•Using WHEN OTHERS THEN NULL to suppress ORA-06512 stacks
    Symptom

    Procedures complete 'successfully' but data is silently corrupted or incomplete. No error visible in application logs. Issues discovered days or weeks later during reconciliation, audits, or customer complaints.

    Fix

    Remove every instance of WHEN OTHERS THEN NULL from your codebase. Use WHEN OTHERS only to add context (parameters, business identifiers) before re-raising. Log every caught exception to the error_log table. Run a codebase audit: grep -rn 'WHEN OTHERS' and flag any handler that does not contain RAISE.

    βœ•Ignoring ORA-06512 line numbers during debugging
    Symptom

    Team searches the entire procedure β€” sometimes hundreds of lines β€” instead of going directly to the specific line. Debugging takes hours instead of minutes.

    Fix

    Use the line number from ORA-06512 to query DBA_SOURCE directly: SELECT text FROM dba_source WHERE name = 'OBJECT_NAME' AND owner = 'OWNER' AND line BETWEEN reported_line - 2 AND reported_line + 2 ORDER BY line. This shows the exact statement with surrounding context. If the package was recompiled since the error, check version control at the deployment timestamp.

    βœ•Catching the wrong named exception for the error code
    Symptom

    Exception handler for NO_DATA_FOUND fires but the actual error was ORA-01403 from a different context (e.g., a trigger or nested call). The handler misinterprets the error and takes the wrong corrective action.

    Fix

    In complex procedures with multiple SELECT INTO statements, use labeled blocks or separate BEGIN/EXCEPTION/END blocks around each query. This ensures each NO_DATA_FOUND handler corresponds to the correct query. Alternatively, use SQLCODE and SQLERRM in a WHEN OTHERS handler with explicit code checks.

Interview Questions on This Topic

  • QHow would you build a production monitoring system that correctly handles ORA-06512 error stacks?SeniorReveal
    First, parse the full error stack using REGEXP_SUBSTR to extract the first ORA- code that is not ORA-06512 β€” this is the primary error. Store the full stack in an error_log table with columns for primary_error_code, full_stack, backtrace, object_name, line_number, session_id, module, action, and timestamp. Alert routing should be based on primary_error_code severity, not ORA-06512 presence. Build dashboards showing error trends by primary error code, object, and time period. Implement error rate thresholds that trigger alerts when specific error codes exceed their historical baseline rate β€” this catches new deployment bugs and data quality changes. Use UTL_CALL_STACK in 12.2+ for structured error metadata instead of string parsing. Finally, integrate with APM tools via REST API from the error_log table for cross-system correlation.
  • QWhat is the difference between ORA-06512 and ORA-06511, and when does each occur?Mid-levelReveal
    ORA-06512 is a stack trace indicator showing where in the call chain an error occurred β€” it always accompanies another error and is never raised independently. ORA-06511 is a specific PL/SQL runtime error meaning 'cursor already open' β€” it is raised when you attempt to OPEN a cursor that is already in the OPEN state. ORA-06511 can itself trigger ORA-06512 if it occurs within a nested procedure call, because ORA-06512 would show the call chain leading to the ORA-06511. The fix for ORA-06511 is to check %ISOPEN before OPEN, or use FOR loops with implicit cursors that Oracle manages automatically. In production, ORA-06511 often indicates retry logic that re-enters a procedure without closing cursors from the previous attempt.
  • QHow do you distinguish ORA-00942 (table does not exist) caused by a missing object from one caused by a missing privilege?Mid-levelReveal
    ORA-00942 can mean either the object truly does not exist or the caller lacks SELECT privilege. Oracle intentionally uses the same error for both cases to prevent information leakage. To distinguish: query DBA_OBJECTS to check if the object exists in any schema. If it exists, query DBA_TAB_PRIVS for direct grants to the current user and DBA_ROLE_PRIVS plus role-level grants to check indirect access. The critical insight is that in definer's rights procedures (the default), role-based grants do not apply β€” only direct grants to the procedure owner are effective. Use AUTHID CURRENT_USER for invoker's rights when the calling user should bring their own privileges. Also check for public synonyms with DBA_SYNONYMS that might mask the correct schema resolution.
  • QWhat is the best practice for handling ORA-04068 (package state discarded) in production with zero-downtime deployments?SeniorReveal
    ORA-04068 occurs when a package body is recompiled and sessions that have initialized package state (global variables, cursor state) are invalidated. For zero-downtime deployments: (1) Implement retry logic at every entry-point procedure β€” catch ORA-04068 specifically with PRAGMA EXCEPTION_INIT and retry up to 3 times with backoff. Re-execution automatically reinitializes the package state. (2) Design packages to be stateless where possible β€” pass state through parameters instead of package globals. (3) Use edition-based redefinition (EBR) in 11gR2+ to deploy new code without invalidating existing sessions. (4) If EBR is not available, coordinate deployments with a connection drain: stop new connections, wait for active sessions to complete, recompile, then resume connections. Log all ORA-04068 occurrences with timestamps to correlate with deployment events.
  • QExplain why DBMS_UTILITY.FORMAT_ERROR_BACKTRACE is preferable to FORMAT_ERROR_STACK for logging ORA-06512 information, and when each is appropriate.SeniorReveal
    FORMAT_ERROR_STACK returns the error message and ORA codes but the line numbers it reports are reset when you re-raise with RAISE β€” the line number changes to the line of the RAISE statement, not the original error location. FORMAT_ERROR_BACKTRACE preserves the original line number where the error first occurred, even through multiple RAISE statements. This makes it essential for debugging in layered architectures where exceptions propagate through several handlers. Best practice is to log both: FORMAT_ERROR_STACK for the error codes and messages, and FORMAT_ERROR_BACKTRACE for the original error location. FORMAT_CALL_STACK adds the complete call chain at the point of logging. In 12.2+, UTL_CALL_STACK provides programmatic access to individual stack frames with typed accessors, which is preferable for structured logging.

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.

πŸ”₯
Naren Founder & Author

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.

← PreviousORA-20001 to ORA-20999: User-Defined Errors and ORA-06512Next β†’How to Use RAISE_APPLICATION_ERROR Properly in Oracle
Forged with πŸ”₯ at TheCodeForge.io β€” Where Developers Are Forged