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
✦ Definition~90s read
What is ORA-06512 Is Not an Error — It's a Stack Trace Indicator?
ORA-06512 is not an error itself—it's a stack trace indicator that Oracle appends to PL/SQL runtime errors to show the call stack. When a stored procedure, function, package, or trigger raises an exception, Oracle prepends the actual error (like ORA-00001 unique constraint violation or ORA-01403 no data found) and then adds one or more ORA-06512 lines, each pointing to a specific line number in a PL/SQL unit.
★
ORA-06512 is Oracle's way of saying "the error happened somewhere in this call stack." It is never the actual problem — it always follows another error that is the real culprit.
This lets you trace exactly where the error originated and how it propagated through nested calls, which is critical for debugging in production systems where you can't attach a debugger. Without it, you'd see only the top-level error with no context on which code path triggered it.
In practice, ORA-06512 appears in nearly every PL/SQL error stack, from data integrity violations (ORA-00001, ORA-02291) to cursor leaks (ORA-01000) and memory exhaustion (ORA-04030). The stack trace format is: ORA-06512: at "SCHEMA.PACKAGE_NAME", line N.
The line number refers to the compiled code, not necessarily your source, so you need to query DBA_SOURCE or ALL_SOURCE to map it back. Tools like DBMS_UTILITY.FORMAT_ERROR_BACKTRACE give you the same info programmatically, but ORA-06512 is what you see in alert logs, application logs, and SQL*Plus output.
You should treat ORA-06512 as a breadcrumb, not a root cause. The real error is always the first line in the stack. Common patterns: ORA-00001 followed by ORA-06512 means a unique key violation in a trigger or procedure; ORA-01403 with ORA-06512 means a SELECT INTO returned no rows in a nested block; ORA-04030 with ORA-06512 means a PL/SQL function exhausted PGA memory.
When you see ORA-06512 without any preceding error, that's impossible—it always follows another ORA- error. If you're logging errors, always capture the full stack, not just the first line, or you'll lose the context needed to fix the bug.
Plain-English First
ORA-06512 is Oracle's way of saying "the error happened somewhere in this call stack." It is never the actual problem — it always follows another error that is the real culprit. Each ORA-06512 line is like a breadcrumb showing the path the error traveled through your code, from the deepest nested call outward to the top-level caller. This page catalogs over 30 common Oracle errors that produce ORA-06512 stack traces, with links to detailed debugging guides for each one. Think of it as an index for production error triage — start here, identify the real error code, then jump to the specific guide.
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.
Why ORA-06512 Is Not an Error — It's a Stack Trace Indicator
ORA-06512 is not an error itself; it's a stack trace line that Oracle appends to any PL/SQL exception to show where the error propagated. It appears when an unhandled exception exits a subprogram (procedure, function, package, trigger) and bubbles up to the caller. The message includes the line number and the object name, e.g., "at line 42 in package PKG_ORDER.PROCESS".
In practice, ORA-06512 always accompanies a real error like ORA-01403 (no data found) or ORA-06502 (value too large). The stack trace can be multiple lines deep, each showing one level of the call stack. The line numbers refer to the source code as stored in ALL_SOURCE, not the compiled bytecode, so they are stable across recompiles unless the source changes.
You should treat ORA-06512 as a debugging gift, not a problem. When you see it in an application log, read the first error (the root cause) and then follow the stack trace to understand the execution path. Ignoring the stack trace and focusing only on the final ORA-06512 line is a common mistake that wastes hours of debugging.
Stack Trace Is Your Friend
ORA-06512 is never the root cause — it's Oracle telling you exactly which line in which procedure threw the real error.
Production Insight
A batch job fails silently at 3 AM because a SELECT INTO in a nested procedure returns no rows, but the outer handler only logs ORA-06512 without the preceding ORA-01403.
The symptom is a partial data load with no actionable error message — operations sees only 'ORA-06512 at line 89' and assumes a generic failure.
Rule of thumb: always log the full error stack (SQLERRM + DBMS_UTILITY.FORMAT_ERROR_BACKTRACE), not just the last line of the trace.
Key Takeaway
ORA-06512 is a stack trace, not an error — always look at the error that precedes it.
The line numbers in ORA-06512 map to ALL_SOURCE line numbers, not compiled offsets.
To capture the full call stack in exception handlers, use DBMS_UTILITY.FORMAT_ERROR_BACKTRACE instead of relying on SQLERRM alone.
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.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
-- 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 extractorCREATEORREPLACEFUNCTION io.thecodeforge.util.parse_primary_error(
p_error_stack INVARCHAR2
) RETURNVARCHAR2DETERMINISTICIS
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-06512LOOP
v_idx := v_idx + 1;
v_match := REGEXP_SUBSTR(
p_error_stack,
'ORA-[0-9]{5}',
1,
v_idx
);
EXITWHEN v_match ISNULL; -- no more codesEXITWHEN v_match != 'ORA-06512'; -- found the real errorENDLOOP;
RETURNNVL(v_match, 'UNKNOWN'); -- defensive fallbackEND parse_primary_error;
/
-- Comprehensive exception handler pattern for production proceduresCREATEORREPLACEPROCEDURE io.thecodeforge.example.guarded_proc IS
v_primary VARCHAR2(20);
BEGIN-- ... business logic ...NULL;
EXCEPTIONWHENOTHERSTHEN
v_primary := io.thecodeforge.util.parse_primary_error(SQLERRM);
INSERTINTO 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 whomSYSTIMESTAMP
);
RAISE; -- ALWAYS re-raise after loggingEND guarded_proc;
/
Stack Trace Reading Order
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.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
-- ORA-00001: unique constraint violation-- Pattern: individual row handling with constraint name extractionCREATEORREPLACEPROCEDURE io.thecodeforge.etl.insert_order(
p_order_id INNUMBER,
p_customer_id INNUMBER,
p_amount INNUMBER
) IS
v_constraint_name VARCHAR2(128);
BEGININSERTINTO io.thecodeforge.orders (
order_id, customer_id, order_amount, created_at
) VALUES (
p_order_id, p_customer_id, p_amount, SYSTIMESTAMP
);
EXCEPTIONWHEN 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 callerRAISE;
END insert_order;
/
-- ORA-02291: integrity constraint violated - parent key not found-- Pattern: pre-validation for FK relationships in batch loadsCREATEORREPLACEPROCEDURE io.thecodeforge.etl.load_order_items IS
v_orphan_count NUMBER;
BEGIN-- Identify orphaned staging records BEFORE attempting insertsINSERTINTO io.thecodeforge.staging.rejected_items (
order_id, product_id, reject_reason, rejected_at
)
SELECT s.order_id, s.product_id,
'Parent order not found', SYSTIMESTAMPFROM io.thecodeforge.staging.raw_items s
WHERENOTEXISTS (
SELECT1FROM io.thecodeforge.orders o
WHERE o.order_id = s.order_id
);
v_orphan_count := SQL%ROWCOUNT;
IF v_orphan_count > 0THEN
io.thecodeforge.logging.log_warning(
'FK_PRECHECK',
v_orphan_count || ' orphaned items moved to rejected_items'
);
ENDIF;
-- Now insert only valid itemsINSERTINTO 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
WHEREEXISTS (
SELECT1FROM 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 checkCREATEORREPLACEPROCEDURE io.thecodeforge.service.delete_customer(
p_customer_id INNUMBER
) IS
v_child_count NUMBER;
BEGINSELECTCOUNT(*) INTO v_child_count
FROM io.thecodeforge.orders
WHERE customer_id = p_customer_id;
IF v_child_count > 0THEN
RAISE_APPLICATION_ERROR(-20010,
'Cannot delete customer ' || p_customer_id ||
': ' || v_child_count || ' orders exist. '
|| 'Archive orders first.');
ENDIF;
DELETEFROM io.thecodeforge.customers
WHERE customer_id = p_customer_id;
END delete_customer;
/
Constraint Error Handling in Production
ORA-00001 includes the constraint name in SQLERRM — extract it with REGEXP_SUBSTR for instant diagnosis
ORA-02291 (parent key not found) and ORA-02292 (child record found) indicate FK violations — pre-validate referential integrity before bulk inserts
Never swallow constraint violations with WHEN OTHERS THEN NULL — they indicate data quality issues that compound silently
Log the constraint name AND the offending key values for every violation — without the key values, diagnosis requires reproducing the load
In batch loads, use SAVEPOINT before each logical unit and ROLLBACK TO SAVEPOINT on violation to skip the bad record without aborting the batch
ORA-12899 (value too large for column) is technically a constraint error too — it means upstream data exceeds your column definition
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.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
-- ORA-01000: maximum open cursors exceeded-- Root cause analysis querySELECT 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
GROUPBY s.sid, s.serial#, s.username, s.program
HAVINGCOUNT(*) > 200-- typical OPEN_CURSORS default is 300ORDERBY open_cursor_count DESC;
-- Leak-proof cursor patternCREATEORREPLACEPROCEDURE io.thecodeforge.etl.process_large_dataset ISCURSOR c_data ISSELECT /*+ PARALLEL(4) */
record_id, payload, created_at
FROM io.thecodeforge.staging.raw_data
WHERE status = 'PENDING'ORDERBY created_at;
TYPE t_batch ISTABLEOF c_data%ROWTYPE;
v_batch t_batch;
v_limit PLS_INTEGER := 5000;
v_total PLS_INTEGER := 0;
BEGINOPEN c_data;
LOOPFETCH c_data BULKCOLLECTINTO v_batch LIMIT v_limit;
EXITWHEN v_batch.COUNT = 0;
FORALL i IN1..v_batch.COUNTINSERTINTO 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-01555COMMIT;
v_total := v_total + v_batch.COUNT;
io.thecodeforge.logging.log_progress(
'PROCESS_LARGE_DATASET',
v_total || ' rows processed'
);
ENDLOOP;
CLOSE c_data;
io.thecodeforge.logging.log_info(
'PROCESS_LARGE_DATASET',
'Completed. Total rows: ' || v_total
);
EXCEPTIONWHENOTHERSTHENIF c_data%ISOPENTHENCLOSE c_data;
ENDIF;
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 usageSELECT tablespace_name,
ROUND(SUM(bytes) / 1024 / 1024) AS undo_mb,
COUNT(*) AS extent_count
FROM dba_undo_extents
GROUPBY tablespace_name;
SELECT name, value
FROM v$parameter
WHERE name IN ('undo_retention', 'undo_tablespace', 'undo_management');
Cursor Leak Prevention Checklist
Always close explicit cursors in the EXCEPTION block — a raised exception skips the normal CLOSE statement
Prefer implicit cursors (FOR rec IN cursor LOOP) — Oracle closes them automatically, even on exception
BULK COLLECT with LIMIT is the correct pattern for large datasets — it controls memory usage and allows periodic COMMIT
Monitor v$open_cursor for trending growth across sessions — a steadily climbing count is a leak
ORA-01555 requires either periodic COMMIT to release undo, larger undo tablespace, or increased UNDO_RETENTION
Never increase OPEN_CURSORS to fix ORA-01000 without first finding the leak — you are just delaying the crash
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.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
-- ORA-01403: NO_DATA_FOUND — robust lookup patternCREATEORREPLACEFUNCTION io.thecodeforge.service.get_customer_name(
p_customer_id INNUMBER
) RETURNVARCHAR2IS
v_name VARCHAR2(200);
BEGINSELECT customer_name INTO v_name
FROM io.thecodeforge.customers
WHERE customer_id = p_customer_id;
RETURN v_name;
EXCEPTIONWHEN NO_DATA_FOUND THEN-- Decision point: is a missing customer a business error or expected?-- Option A: return NULL and let the caller decideRETURNNULL;
-- 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 sizingCREATEORREPLACEPROCEDURE io.thecodeforge.etl.transform_address(
p_raw_address INVARCHAR2,
p_city OUTVARCHAR2, -- target column is VARCHAR2(100)
p_state OUTVARCHAR2-- target column is VARCHAR2(50)
) IS
v_delimiter_pos PLS_INTEGER;
BEGIN
v_delimiter_pos := INSTR(p_raw_address, ',');
IF v_delimiter_pos = 0THEN-- 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);
ENDIF;
IFLENGTH(p_raw_address) > 100THEN
io.thecodeforge.logging.log_warning(
'TRUNCATION',
'Address truncated from ' || LENGTH(p_raw_address) ||
' to 100 chars. Original: ' || SUBSTR(p_raw_address, 1, 200)
);
ENDIF;
END transform_address;
/
-- ORA-06550: compilation error in dynamic SQL — log the SQL textCREATEORREPLACEPROCEDURE io.thecodeforge.util.exec_dynamic(
p_sql INVARCHAR2
) ISBEGINEXECUTEIMMEDIATE p_sql;
EXCEPTIONWHENOTHERSTHEN
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 preventionCREATEORREPLACEFUNCTION io.thecodeforge.util.safe_divide(
p_numerator INNUMBER,
p_denominator INNUMBER,
p_default INNUMBERDEFAULT0
) RETURNNUMBERDETERMINISTICISBEGINIF p_denominator = 0OR p_denominator ISNULLTHENRETURN p_default;
ENDIF;
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.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
-- ORA-04068 detection and automatic retry patternCREATEORREPLACEPROCEDURE 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);
BEGINLOOPBEGIN
io.thecodeforge.core.process_batch; -- the real workEXIT; -- success — leave the retry loopEXCEPTIONWHEN 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 automaticallyELSE
io.thecodeforge.logging.log_error(
'ORA-04068',
'Max retries exhausted after ' || c_max_retries ||
' attempts. Raising to caller.'
);
RAISE;
ENDIF;
END;
ENDLOOP;
END resilient_call;
/
-- ORA-04030 prevention: monitor PGA memory usageSELECT 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 MBORDERBY p.pga_alloc_mem DESC;
-- ORA-04031 diagnosis: shared pool free memory and fragmentationSELECT 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')
ORDERBY 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
GROUPBY force_matching_signature
HAVINGCOUNT(*) > 50ORDERBY versions DESCFETCHFIRST10ROWSONLY;
Resource Error Impact Assessment
ORA-04030 kills the session — no recovery possible within the session; the process may need to be terminated at the OS level if it hangs
ORA-04031 affects ALL sessions — it is a shared pool exhaustion event requiring immediate DBA intervention
ORA-04068 requires only re-execution — the package state reinitializes on the next call; implement retry logic
Monitor PGA memory growth per session to predict ORA-04030 before it crashes production batch jobs
Schedule DDL changes (including package recompilation) during maintenance windows to avoid ORA-04068 during business hours
Set PGA_AGGREGATE_LIMIT to prevent a single runaway session from starving all other sessions of memory
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.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
-- ORA-00060: deadlock retry patternCREATEORREPLACEPROCEDURE io.thecodeforge.service.update_inventory(
p_product_id INNUMBER,
p_quantity INNUMBER
) IS
v_retry_count PLS_INTEGER := 0;
c_max_retries CONSTANT PLS_INTEGER := 3;
c_retry_delay CONSTANTNUMBER := 0.5; -- seconds
e_deadlock EXCEPTION;
PRAGMA EXCEPTION_INIT(e_deadlock, -60);
BEGINLOOPBEGINUPDATE io.thecodeforge.inventory
SET quantity_on_hand = quantity_on_hand - p_quantity,
last_updated = SYSTIMESTAMPWHERE product_id = p_product_id
AND quantity_on_hand >= p_quantity;
IFSQL%ROWCOUNT = 0THEN
RAISE_APPLICATION_ERROR(-20003,
'Insufficient inventory for product ' || p_product_id ||
'. Requested: ' || p_quantity);
ENDIF;
EXIT; -- successEXCEPTIONWHEN 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);
ELSERAISE;
ENDIF;
END;
ENDLOOP;
END update_inventory;
/
-- ORA-00054: lock detection before DDL-- Check for active locks before attempting partition maintenanceSELECT 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'ORDERBY l.locked_mode DESC;
-- Deadlock trace file locationSELECT value AS trace_dir
FROM v$diag_info
WHERE name = 'Default Trace File';
Concurrency Error Response Playbook
ORA-00060 rolls back only the current STATEMENT, not the entire transaction — your transaction may be in a partially committed state
Always check the deadlock trace file in the ADR home — it contains the deadlock graph showing which sessions and rows were involved
Implement retry with exponential backoff for deadlock-prone operations — most deadlocks resolve on retry
ORA-00054 indicates a NOWAIT lock conflict — use WAIT with a timeout instead of NOWAIT for more graceful handling
Prevent deadlocks by establishing a consistent lock acquisition order across all procedures that modify the same tables
Monitor DBA_HIST_ACTIVE_SESS_HISTORY for 'enq: TX - row lock contention' wait events to identify hot rows before deadlocks occur
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.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
-- Distinguish missing object from missing privilege-- (Must be run by a DBA or user with SELECT on DBA_ views)CREATEORREPLACEPROCEDURE io.thecodeforge.util.diagnose_942(
p_object_name INVARCHAR2,
p_schema INVARCHAR2DEFAULTUSER
) IS
v_exists NUMBER;
v_grant_count NUMBER;
BEGIN-- Step 1: does the object exist at all?SELECTCOUNT(*) 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 = 0THEN
DBMS_OUTPUT.PUT_LINE(
'DIAGNOSIS: Object ' || UPPER(p_schema) || '.' ||
UPPER(p_object_name) || ' does not exist.'
);
-- Check for synonyms that might point to itSELECTCOUNT(*) INTO v_exists
FROM dba_synonyms
WHERE synonym_name = UPPER(p_object_name);
IF v_exists > 0THEN
DBMS_OUTPUT.PUT_LINE(
'NOTE: A synonym named ' || UPPER(p_object_name) ||
' exists. Check if it points to the correct schema.'
);
ENDIF;
RETURN;
ENDIF;
-- Step 2: object exists — check direct grantsSELECTCOUNT(*) 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
) LOOPSELECTCOUNT(*) 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 > 0THEN
DBMS_OUTPUT.PUT_LINE(
'WARNING: Grant exists via role ' || rec.granted_role ||
' — this will NOT work inside definer''s rights procedures'
);
ENDIF;
ENDLOOP;
END diagnose_942;
/
-- Deployment grant verification script-- Run after every deployment to catch missing grantsSELECT 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 hoursANDNOTEXISTS (
SELECT1FROM dba_tab_privs p
WHERE p.owner = o.owner
AND p.table_name = o.object_name
AND p.grantee = 'APP_USER' -- your application schema
)
ORDERBY 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.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
-- ORA-01722: invalid number — safe conversion patternCREATEORREPLACEFUNCTION io.thecodeforge.util.safe_to_number(
p_value INVARCHAR2,
p_default INNUMBERDEFAULTNULL
) RETURNNUMBERDETERMINISTICISBEGINRETURN TO_NUMBER(
REGEXP_REPLACE(p_value, '[^0-9.\-]', '') -- strip non-numeric chars
);
EXCEPTIONWHEN VALUE_ERROR THENRETURN p_default;
END safe_to_number;
/
-- ORA-01830/01858/01861: date conversion — defensive parsingCREATEORREPLACEFUNCTION io.thecodeforge.util.safe_to_date(
p_value INVARCHAR2,
p_format INVARCHAR2DEFAULT'YYYY-MM-DD'
) RETURNDATEDETERMINISTICIS
v_date DATE;
BEGIN-- Try the expected format first
v_date := TO_DATE(p_value, p_format);
RETURN v_date;
EXCEPTIONWHENOTHERSTHEN-- Try ISO 8601 fallbackBEGIN
v_date := TO_DATE(p_value, 'YYYY-MM-DD"T"HH24:MI:SS');
RETURN v_date;
EXCEPTIONWHENOTHERSTHEN-- Try US format fallbackBEGIN
v_date := TO_DATE(p_value, 'MM/DD/YYYY');
RETURN v_date;
EXCEPTIONWHENOTHERSTHEN
io.thecodeforge.logging.log_warning(
'DATE_PARSE_FAILURE',
'Cannot parse: ' || SUBSTR(p_value, 1, 100) ||
' with format ' || p_format
);
RETURNNULL;
END;
END;
END safe_to_date;
/
-- Pre-validation: find bad data before it hits your DMLSELECT record_id, amount_text,
CASEWHEN REGEXP_LIKE(amount_text, '^-?[0-9]+(\.[0-9]+)?$')
THEN'VALID'ELSE'INVALID'ENDAS validation_status
FROM io.thecodeforge.staging.raw_transactions
WHERENOT REGEXP_LIKE(NVL(amount_text,'X'), '^-?[0-9]+(\.[0-9]+)?$');
Conversion Error Prevention
ORA-01722 can hide in WHERE clauses — implicit conversion of VARCHAR2 to NUMBER fails on non-numeric rows
Never rely on implicit type conversion — always use explicit TO_NUMBER, TO_DATE with format masks
Pre-validate staging data before DML: use REGEXP_LIKE to identify non-numeric strings, invalid dates, etc.
Date format errors (ORA-01830, ORA-01858, ORA-01861) are caused by NLS_DATE_FORMAT mismatches — always specify the format explicitly
In ETL pipelines, load all external data as VARCHAR2 into staging, validate, then convert during the transform phase
Use DEFAULT ON CONVERSION ERROR (12c+) for inline safe conversion: TO_NUMBER(val DEFAULT 0 ON CONVERSION ERROR)
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.sqlSQL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
-- ORA-30926: fix by deduplicating the source-- BAD: source has duplicate customer_idsMERGEINTO io.thecodeforge.customers tgt
USING io.thecodeforge.staging.customer_updates src
ON (tgt.customer_id = src.customer_id)
WHENMATCHEDTHENUPDATESET tgt.email = src.email
WHENNOTMATCHEDTHENINSERT (customer_id, email)
VALUES (src.customer_id, src.email);
-- Fails with ORA-30926 if staging has duplicate customer_ids-- GOOD: deduplicated sourceMERGEINTO io.thecodeforge.customers tgt
USING (
SELECT customer_id, email
FROM (
SELECT customer_id, email,
ROW_NUMBER() OVER (
PARTITIONBY customer_id
ORDERBY 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)
WHENMATCHEDTHENUPDATESET tgt.email = src.email
WHENNOTMATCHEDTHENINSERT (customer_id, email)
VALUES (src.customer_id, src.email);
-- Pre-check for duplicates before running MERGESELECT customer_id, COUNT(*) AS dup_count
FROM io.thecodeforge.staging.customer_updates
GROUPBY customer_id
HAVINGCOUNT(*) > 1ORDERBY 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.
● Production incidentPOST-MORTEMseverity: high
Alert Storm from Misconfigured ORA-06512 Monitoring
Symptom
PagerDuty 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.
Assumption
The 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 cause
ORA-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.
Fix
1. 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 stack
Alert on the root error, not the stack trace indicator
Build error stack parsers for production monitoring systems and validate them against real trace files
Audit WHEN OTHERS THEN NULL handlers quarterly — they are silent data corruption vectors
Run reconciliation checks on critical data pipelines independently of application-level error logging
Production debug guideSymptom-to-action mapping for stack trace debugging in production environments6 entries
Symptom · 01
ORA-06512 appears in the alert log with no preceding error visible
→
Fix
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.
Symptom · 02
Multiple ORA-06512 lines in a single error stack
→
Fix
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.
Symptom · 03
Application catches ORA-06512 but not the real error
→
Fix
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.
Symptom · 04
ORA-06512 with line number 0 or no line number
→
Fix
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.
Symptom · 05
ORA-06512 stack references a procedure that has been recompiled since the error
→
Fix
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.
Symptom · 06
ORA-06512 appears intermittently under high concurrency
→
Fix
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 Stack Parsing Cheat SheetCommands 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 action
Extract 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 now
Parse 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 action
Query 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 now
Recompile 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 action
Join 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 now
Use 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.
30+ Oracle Errors That Trigger ORA-06512
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
1
ORA-06512 is a stack trace indicator
never the actual error to fix; always parse the preceding ORA code
2
Always parse the first non-06512 ORA code from the error stack for alerting and triage routing
3
30+ common Oracle errors trigger ORA-06512
categorize by type (data integrity, cursor, logic, memory, security, conversion, concurrency, DML) for efficient triage
4
Log the full error stack using FORMAT_ERROR_STACK, FORMAT_ERROR_BACKTRACE, and FORMAT_CALL_STACK alongside session context, object name, and business identifiers
5
Build automated stack parsers at the monitoring layer
never rely on manual reading for production triage
6
Audit WHEN OTHERS THEN NULL handlers quarterly
they are silent data corruption vectors that hide the errors ORA-06512 would otherwise reveal
7
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
6 patterns
×
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 PREP · PRACTICE MODE
Interview Questions on This Topic
Q01SENIOR
How would you build a production monitoring system that correctly handle...
Q02SENIOR
What is the difference between ORA-06512 and ORA-06511, and when does ea...
Q03SENIOR
How do you distinguish ORA-00942 (table does not exist) caused by a miss...
Q04SENIOR
What is the best practice for handling ORA-04068 (package state discarde...
Q05SENIOR
Explain why DBMS_UTILITY.FORMAT_ERROR_BACKTRACE is preferable to FORMAT_...
Q01 of 05SENIOR
How would you build a production monitoring system that correctly handles ORA-06512 error stacks?
ANSWER
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.
Q02 of 05SENIOR
What is the difference between ORA-06512 and ORA-06511, and when does each occur?
ANSWER
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.
Q03 of 05SENIOR
How do you distinguish ORA-00942 (table does not exist) caused by a missing object from one caused by a missing privilege?
ANSWER
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.
Q04 of 05SENIOR
What is the best practice for handling ORA-04068 (package state discarded) in production with zero-downtime deployments?
ANSWER
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.
Q05 of 05SENIOR
Explain why DBMS_UTILITY.FORMAT_ERROR_BACKTRACE is preferable to FORMAT_ERROR_STACK for logging ORA-06512 information, and when each is appropriate.
ANSWER
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.
01
How would you build a production monitoring system that correctly handles ORA-06512 error stacks?
SENIOR
02
What is the difference between ORA-06512 and ORA-06511, and when does each occur?
SENIOR
03
How do you distinguish ORA-00942 (table does not exist) caused by a missing object from one caused by a missing privilege?
SENIOR
04
What is the best practice for handling ORA-04068 (package state discarded) in production with zero-downtime deployments?
SENIOR
05
Explain why DBMS_UTILITY.FORMAT_ERROR_BACKTRACE is preferable to FORMAT_ERROR_STACK for logging ORA-06512 information, and when each is appropriate.
SENIOR
FAQ · 7 QUESTIONS
Frequently Asked Questions
01
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.
Was this helpful?
02
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.
Was this helpful?
03
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.
Was this helpful?
04
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.
Was this helpful?
05
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).
Was this helpful?
06
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.
Was this helpful?
07
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.