SQL Developer's built-in PL/SQL debugger can trap exceptions at the exact line before ORA-06512 propagates up the call chain — eliminating manual stack trace parsing entirely
Key component 1: Exception breakpoints pause execution the moment any Oracle error is raised, before exception handlers run
Key component 2: The Call Stack window shows the full nested procedure chain triggering ORA-06512 as an interactive, clickable view — not a static text dump
Key component 3: The Variables window reveals the exact runtime values (NULLs, truncated strings, bad numbers) that caused the error at the moment it fired
Performance insight: remote debugging adds 200–500 ms latency per breakpoint hit versus local connections — acceptable for diagnosis, painful if you leave it on for hours
Production insight: developers who skip debugger setup and fall back to DBMS_OUTPUT loops waste 3–5x more time on ORA-06512 triage per incident
Biggest mistake: reading the error stack manually and correlating line numbers by hand instead of letting the debugger halt at the source — line numbers shift every time you edit the procedure, invalidating all your manual work
Second biggest mistake: forgetting to compile with DEBUG before starting the session — breakpoints are silently ignored and the debugger appears broken
Plain-English First
SQL Developer has a built-in PL/SQL debugger that works like the debuggers you find in IntelliJ, VS Code, or Eclipse. You can set breakpoints, step through code line by line, inspect variable values at runtime, and — most importantly for ORA-06512 — set exception breakpoints that pause execution the instant an error is raised, before it propagates up the call stack and becomes a cryptic multi-line trace.
Without the debugger, debugging ORA-06512 looks like this: read a stack trace, count line numbers, open the source package, find line 142, guess what was wrong with the data, add a DBMS_OUTPUT.PUT_LINE, redeploy, re-execute, read the output, repeat. Each cycle takes 15–30 minutes. Line numbers shift every time you edit the code.
With the debugger configured correctly, it looks like this: set an exception breakpoint for ORA-01403, run the failing procedure, wait 30 seconds, click on Frame 0 in the Call Stack window, look at the Variables window — the NULL parameter is right there. Total time: under 5 minutes, first attempt.
This tutorial covers exactly how to get there: privileges, DEBUG compilation, local and remote setup, exception breakpoints, Call Stack navigation, and variable inspection. It also covers what to do when you cannot use the debugger — production environments where interactive debugging is off the table.
ORA-06512 stack traces are informative but require manual correlation work: line numbers must be matched to source code, and that source code must be at the exact version deployed when the error occurred. Any edit between error and diagnosis shifts the line numbers. SQL Developer's debugger eliminates this entire class of friction by halting execution at the exact point where an exception is raised and presenting the full call chain as a navigable, clickable interface.
The Call Stack window is the debugger's answer to ORA-06512. Each frame in the window corresponds to one ORA-06512 line in the textual stack — except you can click on any frame to jump directly to that line in the source and inspect every variable at that scope level. The Variables window shows exactly what values were in play at the moment the error fired. Exception breakpoints mean you do not need to know in advance which line to break on — you tell the debugger 'halt on ORA-01403' and it finds the line for you.
Engineers who configure exception breakpoints before starting a debugging session consistently reduce ORA-06512 diagnosis time from 30 minutes to under 5 minutes for known error codes, and from hours to under 20 minutes for unknown error codes in deep nested call chains. This guide covers the complete setup path: privilege grants, DEBUG compilation, local and remote connection configuration, exception breakpoint configuration, Call Stack navigation, variable inspection, and the production fallback pattern for environments where interactive debugging is not available.
Privileges and DEBUG Compilation Setup
Two things must be true before SQL Developer's debugger can work: the developer must have the correct Oracle privileges, and every procedure in the call chain must be compiled with the DEBUG flag. Missing either one produces silent failures — breakpoints are ignored without error messages, and variables show as unreadable without explanation.
Privilege requirements are straightforward: DEBUG CONNECT SESSION allows the session to initiate a debug connection; DEBUG ANY PROCEDURE allows setting breakpoints in any schema's compiled objects. For least-privilege environments, you can grant DEBUG ON specific objects instead of DEBUG ANY PROCEDURE — useful when developers should only debug objects they own or have been explicitly granted access to.
DEBUG compilation embeds symbol table information into the compiled object. This is what allows the debugger to map machine instructions back to source lines and variable names. Without it, the debugger cannot resolve line numbers or variable names, so breakpoints have nothing to bind to. The compilation change is permanent until you recompile without the DEBUG flag — which you must do before promoting to production.
The DEBUG flag slightly increases object size (10–15%) due to the embedded symbol tables, but has zero effect on runtime performance when no debugger is attached. The overhead only materializes when the debugger is actively connected and a breakpoint is hit.
debug_setup.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
-- ===================================================-- Step 1: Grant debugging privileges (run as DBA)-- ===================================================-- Option A: Full debug access (appropriate for senior developers)GRANTDEBUGCONNECTSESSIONTO your_developer_user;
GRANTDEBUGANYPROCEDURETO your_developer_user;
-- Option B: Least-privilege debug access (specific objects only)GRANTDEBUGCONNECTSESSIONTO your_developer_user;
GRANTDEBUGON io.thecodeforge.report.generator TO your_developer_user;
GRANTDEBUGON io.thecodeforge.report.batch_runner TO your_developer_user;
GRANTDEBUGON io.thecodeforge.scheduler.nightly_job TO your_developer_user;
-- Verify privileges were grantedSELECT grantee, privilege, admin_option
FROM dba_sys_privs
WHERE grantee = 'YOUR_DEVELOPER_USER'AND privilege IN ('DEBUG CONNECT SESSION', 'DEBUG ANY PROCEDURE')
ORDERBY privilege;
-- ===================================================-- Step 2: Compile target objects with DEBUG flag-- Run for every object in the call chain-- ===================================================-- Compile individual proceduresALTERPROCEDURE io.thecodeforge.payment.reconcile_batch COMPILEDEBUG;
ALTERPROCEDURE io.thecodeforge.scheduler.nightly_job COMPILEDEBUG;
-- Compile packages: spec first, then body-- The body is where procedure code lives — the body must have DEBUGALTERPACKAGE io.thecodeforge.report.generator COMPILEDEBUG;
ALTERPACKAGE io.thecodeforge.report.generator COMPILEBODYDEBUG;
ALTERPACKAGE io.thecodeforge.report.batch_runner COMPILEDEBUG;
ALTERPACKAGE io.thecodeforge.report.batch_runner COMPILEBODYDEBUG;
-- ===================================================-- Step 3: Verify DEBUG compilation status-- ===================================================SELECT p.object_name,
p.object_type,
p.procedure_name,
CASEWHEN p.debuginfo = 'YES'THEN'DEBUG ENABLED — breakpoints will work'ELSE'DEBUG MISSING — recompile with DEBUG flag'ENDAS debug_status
FROM user_procedures p
WHERE p.object_name IN (
'RECONCILE_BATCH',
'GENERATOR',
'BATCH_RUNNER',
'NIGHTLY_JOB'
)
ORDERBY p.object_name, p.procedure_name;
-- ===================================================-- Step 4: Revert to non-debug compilation-- ALWAYS run this before promoting to any environment-- beyond your local dev schema-- ===================================================ALTERPROCEDURE io.thecodeforge.payment.reconcile_batch COMPILE;
ALTERPACKAGE io.thecodeforge.report.generator COMPILE;
ALTERPACKAGE io.thecodeforge.report.generator COMPILEBODY;
ALTERPACKAGE io.thecodeforge.report.batch_runner COMPILE;
ALTERPACKAGE io.thecodeforge.report.batch_runner COMPILEBODY;
ALTERPROCEDURE io.thecodeforge.scheduler.nightly_job COMPILE;
-- Verify revert: all should show 'DEBUG MISSING' after revertSELECT object_name, object_type,
CASEWHEN debuginfo = 'YES'THEN'WARNING: STILL IN DEBUG MODE — DO NOT DEPLOY'ELSE'OK: Non-debug compilation confirmed'ENDAS deployment_status
FROM user_procedures
WHERE object_name IN (
'RECONCILE_BATCH', 'GENERATOR', 'BATCH_RUNNER', 'NIGHTLY_JOB'
)
ORDERBY object_name;
DEBUG Compilation — What It Does and Does Not Change
DEBUG adds symbol table entries to the compiled object: source line-to-bytecode mappings and variable name metadata
Object size increases 10–15% due to embedded symbol tables — negligible in practice
Runtime performance is identical when no debugger is attached — zero overhead for normal execution
When the debugger is attached and hits a breakpoint, it uses the symbol table to map the current bytecode position back to a source line and resolve variable names
DEBUG info is stored in the data dictionary (SYS.IDL_SB4$ and related tables), not in the SGA — it does not consume PGA or shared pool memory
Always revert to non-debug compilation before deploying — debug objects expose internal variable names in USER_PROCEDURES and the symbol table is unnecessary overhead in production
Production Insight
DEBUG compilation must be reverted before production deployment — this is not optional.
Debug objects expose internal variable names through the data dictionary which is a security concern for procedures that handle sensitive data like PII, payment information, or credentials.
Use separate test environment schemas for all debugging sessions. If your team shares a test environment, coordinate DEBUG compilation changes to avoid confusing other developers who may be running the non-debug version.
Key Takeaway
Both conditions must be true: correct privileges AND DEBUG compilation. Missing either produces silent failures — no error, no breakpoints, no explanation.
Compile every procedure in the call chain with DEBUG, not just the one you think is failing.
Always revert to non-debug compilation before promoting to any shared or production environment.
Add a pre-deploy CI/CD check that fails if any procedure shows debuginfo = 'YES'.
Debug Privilege and Compilation Strategy
IfDeveloper debugging only their own schema objects
→
UseGRANT DEBUG CONNECT SESSION — sufficient for owned objects. Compile owned procedures with DEBUG. No DEBUG ANY PROCEDURE needed.
IfDeveloper debugging shared package code in another schema
→
UseGRANT DEBUG ON specific_package TO developer — least-privilege. Or GRANT DEBUG ANY PROCEDURE for unrestricted access across all schemas.
IfTeam shares a test environment and multiple developers may debug concurrently
→
UseCoordinate DEBUG compilation. One developer's DEBUG recompile affects all sessions using that object. Consider personal schemas for debugging.
IfCI/CD pipeline needs to verify DEBUG compilation was reverted before deploy
→
UseAdd a pre-deploy check: SELECT COUNT(*) FROM user_procedures WHERE debuginfo = 'YES' — fail the pipeline if count > 0.
IfProduction debugging is required for an unresolvable issue
→
UseDo NOT use SQL Developer debugger on production. Use a read-only replica or a production-mirrored test environment with the same data. Apply enhanced FORMAT_CALL_STACK logging instead.
Setting Exception Breakpoints for ORA-06512
Exception breakpoints are the highest-leverage feature in SQL Developer's debugger for ORA-06512 diagnosis. A regular line breakpoint requires you to know in advance which line to halt at. An exception breakpoint requires only that you know which error code is being raised — the debugger finds the line for you.
When an exception breakpoint fires, the debugger halts before the exception handler executes. This means you see the full pre-handler state: the exact line that raised the error, the values of all local variables, the state of any cursors, and the full Call Stack showing how execution reached that point. This is the state you need for diagnosis — not the state inside the WHEN clause after the error has already been caught.
The most common exception breakpoints for ORA-06512 investigation are ORA-01403 (NO_DATA_FOUND from SELECT INTO), ORA-00001 (unique constraint violated), ORA-06502 (VALUE_ERROR from type conversion), and ORA-01722 (invalid number from implicit conversion). If you do not know which error is causing the ORA-06512 stack, start with a general approach: set a breakpoint at the RAISE or the DML statement in the outermost procedure shown in the ORA-06512 stack, then use Step Into to drill down to the source.
exception_breakpoints.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
-- ===================================================-- SQL Developer Exception Breakpoint Setup-- UI Path: View > Breakpoints > right-click > Add Exception Breakpoint-- ===================================================-- Common error numbers for exception breakpoints:-- Enter these in the 'Error Number' field (without ORA- prefix):---- 1403 — ORA-01403: NO_DATA_FOUND (SELECT INTO returned no rows)-- 1 — ORA-00001: unique constraint violated-- 1422 — ORA-01422: TOO_MANY_ROWS (SELECT INTO returned multiple rows)-- 6502 — ORA-06502: VALUE_ERROR (type conversion failure)-- 1722 — ORA-01722: invalid number (implicit conversion failure)-- 2291 — ORA-02291: FK parent key not found-- 60 — ORA-00060: deadlock detected-- 1555 — ORA-01555: snapshot too old-- ===================================================-- Execution block: run this to trigger the debug session-- The exception breakpoint will fire when the error occurs-- ===================================================DECLARE-- Declare any test variables needed
v_batch_id NUMBER := 12345;
v_run_date DATE := DATE'2026-04-14';
BEGIN-- For remote debugging: connect SQL Developer first, then call CONNECT_TCP-- For local debugging: skip CONNECT_TCP and just call the procedure-- Call the failing procedure — debugger halts when exception fires
io.thecodeforge.payment.reconcile_batch(
p_batch_id => v_batch_id,
p_run_date => v_run_date
);
END;
/
-- ===================================================-- What you see when the exception breakpoint fires:-- ===================================================-- 1. Execution halts at the EXACT line that raised ORA-01403-- (e.g., line 142 in io.thecodeforge.report.generator)-- 2. That line is highlighted in the source editor-- 3. Variables window shows all in-scope variables:-- p_report_type = 'QUARTERLY_ADJUSTMENT' <-- the problem-- p_run_date = 2026-04-14-- v_count = 0 <-- why NO_DATA_FOUND-- 4. Call Stack window shows:-- Frame 0: IO.THECODEFORGE.REPORT.GENERATOR.GET_TEMPLATE line 142-- Frame 1: IO.THECODEFORGE.REPORT.BATCH_RUNNER.RUN line 87-- Frame 2: IO.THECODEFORGE.SCHEDULER.NIGHTLY_JOB.EXECUTE line 23---- Click Frame 1 -> jumps to batch_runner line 87,-- Variables window updates to show batch_runner's local variables-- Click Frame 2 -> jumps to nightly_job line 23,-- Variables window updates to show scheduler's local variables-- ===================================================-- Conditional exception breakpoint (for intermittent errors)-- Set condition in Breakpoints > Properties > Condition:-- ===================================================-- Condition expression examples (enter in Breakpoints Properties dialog):-- p_report_type = 'QUARTERLY_ADJUSTMENT' -- only fire for this report type-- p_batch_id > 10000 -- only fire for large batch IDs-- v_count = 0 -- only fire when count is zero-- p_customer_id IS NULL -- only fire for NULL customer
Exception breakpoints fire BEFORE the exception handler executes — you see the pre-handler state, not the state inside the WHEN clause
The debugger halts at the RAISE or the failing statement (SELECT INTO, DML, arithmetic), not at the beginning of the WHEN clause
If an exception handler catches the error and does NOT re-raise, the debugger resumes after the handler completes — the exception does not propagate, and the ORA-06512 stack never forms
Multiple exception breakpoints can be active simultaneously — useful when you are not sure which error code is being raised
Disable exception breakpoints when not actively debugging — they fire on every matching exception, including benign ones in library code, which makes normal development painful
Exception breakpoints fire even for exceptions caught by WHEN OTHERS — the breakpoint is on the raise, not on the propagation
Production Insight
Exception breakpoints fire before exception handlers execute — this is the most important behavioral detail to understand.
The pre-handler state is exactly what you need: the values that caused the error, before any recovery logic modified them.
If your exception handler logs the error and then NULLs out the offending variable as part of cleanup, you would never see the bad value in the log — but the debugger would show it at the breakpoint.
Key Takeaway
Exception breakpoints are the highest-leverage debugger feature for ORA-06512 — they find the failing line without you needing to know it in advance.
The breakpoint fires before the exception handler, giving you the pre-error variable state.
Set multiple exception breakpoints when you are uncertain which error code is being raised.
Use conditional breakpoints for intermittent errors to avoid false stops on unrelated exceptions.
Breakpoint Selection Strategy
IfORA-06512 stack shows a specific error code (e.g., ORA-01403)
→
UseSet exception breakpoint for that specific error number (1403). Fastest path — debugger finds the line automatically.
IfORA-06512 stack shows only ORA-06512 lines with no preceding error visible
→
UseThe preceding error was truncated. Set exception breakpoints for the most likely candidates: 1403, 1, 6502, 1722. Run and see which one fires.
IfError occurs only under specific data conditions (intermittent)
→
UseUse conditional exception breakpoint. Set the condition to the known triggering data value. Run the job repeatedly until the condition fires.
IfError source is in a trigger, not a procedure
→
UseSet a line breakpoint inside the trigger body. Perform the triggering DML. Exception breakpoints work in triggers but Call Stack may show limited trigger context.
IfError source is in dynamic SQL (EXECUTE IMMEDIATE)
→
UseSet a line breakpoint before the EXECUTE IMMEDIATE. Inspect v_sql variable to see the generated SQL. Dynamic SQL errors cannot be directly halted with exception breakpoints.
Navigating the Call Stack Window
The Call Stack window is the debugger's visual equivalent of the ORA-06512 textual stack trace. When an exception breakpoint fires or a regular breakpoint halts execution, the Call Stack window shows every procedure frame on the current execution stack — the same information as the ORA-06512 lines, but interactive.
Each frame in the Call Stack corresponds to one ORA-06512 line. Frame 0 is the deepest call — the procedure where execution is currently halted. The final frame is the outermost caller (often the anonymous block or the application entry point). Clicking on any frame does two things simultaneously: the source editor jumps to that line, and the Variables window updates to show the local variables at that scope level. This is the key capability — you can inspect the state of every level of the call chain, not just the frame where the error occurred.
This eliminates the most time-consuming manual debugging step: reading ORA-06512 line numbers, finding the correct source file at the correct version, counting to the right line, and then guessing what the variable values were. The debugger shows all of this without guessing.
call_stack_navigation.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
-- ===================================================-- Textual ORA-06512 stack (what you see WITHOUT the debugger)-- ===================================================-- ORA-01403: no data found-- ORA-06512: at "IO.THECODEFORGE.REPORT.GENERATOR", line 142-- ORA-06512: at "IO.THECODEFORGE.REPORT.BATCH_RUNNER", line 87-- ORA-06512: at "IO.THECODEFORGE.SCHEDULER.NIGHTLY_JOB", line 23-- ORA-06512: at line 1---- Manual work required:-- 1. Open io.thecodeforge.report.generator source-- 2. Navigate to line 142-- 3. Guess what the variable values were-- 4. Open io.thecodeforge.report.batch_runner, go to line 87-- 5. Guess what was passed as arguments-- 6. Repeat for nightly_job line 23-- 7. Make a change, redeploy, re-run the batch, wait 40 minutes-- Total: 2–4 hours per debugging cycle-- ===================================================-- Call Stack window (what you see WITH the debugger)-- ===================================================-- Frame 0: IO.THECODEFORGE.REPORT.GENERATOR.GET_TEMPLATE line 142-- [highlighted in source editor — this is where execution halted]-- Frame 1: IO.THECODEFORGE.REPORT.BATCH_RUNNER.PROCESS_TYPE line 87-- Frame 2: IO.THECODEFORGE.SCHEDULER.NIGHTLY_JOB.EXECUTE line 23-- Frame 3: anonymous block line 5---- Interactive actions:-- Click Frame 0 -> source editor shows generator line 142,-- Variables window shows:-- p_report_type = 'QUARTERLY_ADJUSTMENT'-- p_run_date = 2026-04-14-- v_template_id = <NULL> <-- the problem---- Click Frame 1 -> source editor shows batch_runner line 87,-- Variables window shows:-- v_report_types = ['MONTHLY','QUARTERLY_ADJUSTMENT','WEEKLY']-- v_current_type = 'QUARTERLY_ADJUSTMENT'-- v_batch_id = 12345---- Click Frame 2 -> source editor shows nightly_job line 23,-- Variables window shows:-- v_run_date = 2026-04-14-- v_job_name = 'NIGHTLY_REPORT_BATCH'---- Total: 4 minutes from breakpoint fire to root cause identified-- No redeployment, no re-execution, no guessing-- ===================================================-- Read the Call Stack correctly: bottom is the outermost caller-- ===================================================-- Frame 3 (anonymous block, line 5) — the entry point (top-level caller)-- called Frame 2 (nightly_job, line 23)-- called Frame 1 (batch_runner, line 87)-- called Frame 0 (generator, line 142) — where the error occurred---- This matches the ORA-06512 reading order:-- The actual error is at the TOP of the ORA-06512 text stack-- and at Frame 0 (the BOTTOM) of the Call Stack window.-- Both read: innermost call first, outermost caller last.-- ===================================================-- Stepping through the call chain manually-- (use when exception breakpoint fires at an unexpected location)-- ===================================================-- F7 (Step Into) — enter the next called procedure-- F8 (Step Over) — execute the next line without entering sub-calls-- F9 (Resume) — continue execution until the next breakpoint-- Shift+F7 (Step Out) — finish the current procedure and return to caller---- Recommended ORA-06512 debugging sequence:-- 1. Set exception breakpoint-- 2. Run the failing code-- 3. When breakpoint fires, inspect Frame 0 variables-- 4. Click Frame 1 to understand the calling context-- 5. Click Frame 2 to understand the entry conditions-- 6. Root cause is usually apparent within 3 frames
Call Stack Window vs. ORA-06512 Text Stack — Same Data, Different Interface
Each Call Stack frame maps to one ORA-06512 line — Frame 0 is the innermost (deepest) call, matching the first ORA-06512 line after the actual error
Clicking a frame navigates the source editor to that exact line at the exact version currently compiled — no line-number drift
The Variables window updates to show the scope of the clicked frame — you can inspect each procedure's local variables independently
Frame 0 is always the most valuable starting point — it is where the error occurred and where the offending variable value will be visible
The Call Stack window does not disappear after clicking through frames — you can navigate back to Frame 0 at any time
Production Insight
The Call Stack window replaces every manual step of ORA-06512 debugging: finding the source file, navigating to the line number, and guessing at variable values.
In a nested procedure chain with 4+ levels, the manual approach takes 45–90 minutes per cycle. The debugger takes under 5 minutes per session because you skip straight to the variable values.
Train your team to click Frame 1 and Frame 2 as a reflex after every exception breakpoint — the root cause is often not in Frame 0 itself, but visible in the calling context at Frame 1.
Key Takeaway
The Call Stack window is the interactive equivalent of ORA-06512 text lines — same data, fully navigable.
Frame 0 is the innermost call and the error source. Click frames upward to understand the calling context.
The Variables window updates per-frame — inspect each scope independently to find where bad data entered the call chain.
Variable Inspection and Watch Expressions
The Variables and Watch windows are where debugging converts from 'I know where it failed' to 'I know why it failed.' After the Call Stack window shows you which line raised the error, the Variables window shows you what values were in play at that moment — the exact data state that caused the exception.
For ORA-06512 debugging, variable inspection targets the inputs to the failing operation. For a SELECT INTO that raised ORA-01403, inspect the WHERE clause parameters — one of them is likely NULL, the wrong value, or mismatched in type. For an ORA-06502 VALUE_ERROR on a string assignment, inspect the source string's length against the target variable's declared size. For ORA-01722, inspect the string being passed to TO_NUMBER — it contains a character that is not numeric.
The Watch window allows custom expressions that the Variables window does not show by default: SQLCODE, SQLERRM, cursor attributes like v_cursor%ROWCOUNT, and collection properties like v_collection.COUNT. These expressions are evaluated on every step, so keep the Watch window lean — too many expressions slow down remote debugging sessions.
When the Variables window cannot show a value (package globals are only visible when you are in that package's frame), use the Watch window with a fully qualified expression: io.thecodeforge.config_pkg.v_current_env evaluates the package global directly.
variable_inspection.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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
-- ===================================================-- Watch window expressions for ORA-06512 debugging-- Add these via: Watch window > right-click > Add Watch-- ===================================================-- Universal: current error stateSQLCODESQLERRM-- ORA-01403 (NO_DATA_FOUND): inspect SELECT INTO parameters
p_report_type -- is this NULL or an unrecognized value?
p_customer_id -- is this NULL?
v_count -- is this 0 when it should be > 0?-- ORA-01422 (TOO_MANY_ROWS): confirm uniqueness assumption
v_customer_count -- this should be 1, might be > 1-- ORA-06502 (VALUE_ERROR): length and type checksLENGTH(v_string_var) -- is this longer than the target VARCHAR2?DUMP(v_string_var) -- raw bytes — reveals encoding issuesLENGTH(v_description) -- compare against column definition length-- ORA-01722 (invalid number): check string content
v_amount_str -- does this contain '$', ',', or spaces?
REGEXP_REPLACE(v_amount_str, '[^0-9.\-]', '') -- stripped version-- ORA-00001 (unique constraint): see the duplicate key values
p_order_id -- what value is being inserted as PK?
p_batch_id || '_' || p_seq -- composite key components-- Cursor state inspection
v_cursor%ROWCOUNT-- how many rows fetched so far?
v_cursor%ISOPEN-- is the cursor still open?
v_collection.COUNT-- number of elements in collection
v_collection.FIRST-- first index (NULL if empty)
v_collection.LAST-- last index-- Conditional expressions for contextCASEWHEN p_report_type ISNULLTHEN'NULL_INPUT'ELSE p_report_type ENDCASEWHEN v_count = 0THEN'NO_TEMPLATE_FOUND'ELSE TO_CHAR(v_count) || '_FOUND'END-- ===================================================-- Programmatic variable logging for post-mortem analysis-- Use this pattern when SQL Developer debugger is unavailable-- (automated batch jobs, production debugging)-- ===================================================CREATEORREPLACEPROCEDURE io.thecodeforge.debug.log_variables(
p_procedure_name INVARCHAR2,
p_context INCLOB
) ISPRAGMA AUTONOMOUS_TRANSACTION;
BEGININSERTINTO io.thecodeforge.debug.variable_snapshot (
snapshot_id,
procedure_name,
context_dump,
call_stack,
session_id,
db_user,
created_at
) VALUES (
io.thecodeforge.debug.snapshot_seq.NEXTVAL,
p_procedure_name,
p_context,
DBMS_UTILITY.FORMAT_CALL_STACK, -- capture the full stack at log time
SYS_CONTEXT('USERENV', 'SESSIONID'),
SYS_CONTEXT('USERENV', 'SESSION_USER'),
SYSTIMESTAMP
);
COMMIT;
END log_variables;
/
-- Usage: add before a suspect SELECT INTO to capture pre-failure stateCREATEORREPLACEPROCEDURE io.thecodeforge.report.generator(
p_report_type INVARCHAR2,
p_run_date INDATE
) IS
v_template_id NUMBER;
v_count NUMBER;
BEGIN-- Capture state before the suspect operation-- Remove this after debugging — it adds an autonomous transaction per call
io.thecodeforge.debug.log_variables(
p_procedure_name => 'GENERATOR.GET_TEMPLATE',
p_context =>
'p_report_type=' || NVL(p_report_type, 'NULL') ||
', p_run_date=' || TO_CHAR(p_run_date, 'YYYY-MM-DD') ||
', session=' || SYS_CONTEXT('USERENV', 'SESSIONID')
);
-- The suspect SELECT INTOSELECT template_id INTO v_template_id
FROM io.thecodeforge.report.templates
WHERE report_type = p_report_type -- p_report_type = 'QUARTERLY_ADJUSTMENT'AND effective_date <= p_run_date -- and this date comparisonAND active_flag = 'Y';
-- ... rest of procedure ...NULL;
EXCEPTIONWHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20103,
'No active template found for report type: ' ||
NVL(p_report_type, 'NULL') ||
' as of ' || TO_CHAR(p_run_date, 'YYYY-MM-DD'));
END generator;
/
Variable Inspection — What to Look For by Error Code
ORA-01403 (NO_DATA_FOUND): inspect every WHERE clause parameter — look for NULL values, type mismatches, or values that do not exist in the referenced table
ORA-01422 (TOO_MANY_ROWS): inspect the WHERE clause — a uniqueness assumption is violated; check what makes the query return multiple rows
ORA-06502 (VALUE_ERROR): use LENGTH() on the source and compare against the target variable's declared size; use DUMP() to reveal encoding issues and non-printable characters
ORA-01722 (invalid number): inspect the string being converted — look for currency symbols ($, £), commas, spaces, or leading/trailing whitespace
ORA-00001 (unique constraint violated): inspect the values being inserted for the constrained columns — the duplicate key is one of those values
ORA-02291 (FK parent not found): inspect the foreign key column value — either it is NULL (NOT NULL constraint might be missing) or the parent record does not exist
Production Insight
The Variables window shows what values were in play at the moment the error fired — this is the single most valuable piece of information for diagnosis.
Add SQLCODE and SQLERRM to the Watch window before starting any debug session — they show the current error state in real time as you step through the code.
Use DUMP() for conversion errors: it shows the raw byte content of a VARCHAR2 variable, revealing non-printable characters, unexpected encoding, or invisible trailing spaces that cause ORA-06502 and ORA-01722.
Key Takeaway
The Variables window shows the exact runtime values that caused the error — this converts 'where it failed' into 'why it failed.'
Add SQLCODE, SQLERRM, and parameter values to the Watch window before starting every debug session.
Use DUMP() for conversion errors — invisible characters and encoding issues are only visible at the byte level.
Remote Debugging for Test and Staging Environments
Remote debugging connects SQL Developer running on your workstation to a PL/SQL session running on a different database host. This is the correct approach for errors that only reproduce in test or staging environments — which is most of them, since local developer schemas rarely have production-volume data or the exact configuration that triggers the error.
The architecture is slightly counterintuitive: the database session initiates the connection outbound to SQL Developer, not the other way around. DBMS_DEBUG_JDWP.CONNECT_TCP is called from inside the database session, pointing at the SQL Developer host and port. SQL Developer listens on that port and attaches when the call is received. Once attached, both sides communicate over the JDWP (Java Debug Wire Protocol) connection — the same protocol used by Java debuggers.
The firewall implications follow from this architecture: the database server must be able to reach the SQL Developer host on the debug port (default 4000) via outbound TCP. Many corporate environments allow outbound TCP from database servers by default but restrict inbound — which makes this configuration firewall-friendly compared to alternatives where the debugger initiates the connection.
Remote debugging adds 200–500 ms latency per breakpoint interaction under normal network conditions. This is acceptable for diagnosis but becomes painful for step-by-step execution. Use Step Over (F8) rather than Step Into (F7) whenever possible to skip library code you do not need to inspect, and keep the Watch window expression count minimal to reduce per-step evaluation overhead.
remote_debugging.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
95
96
97
98
99
100
101
102
103
-- ===================================================-- Remote debugging setup: overview of the connection flow-- ===================================================---- 1. SQL Developer (your workstation) starts listening on port 4000-- Menu: Run > Remote Debug... > enter listening port > OK-- SQL Developer is now waiting for an inbound JDWP connection---- 2. Target database session calls CONNECT_TCP pointing at SQL Developer-- The call BLOCKS until SQL Developer attaches or timeout expires---- 3. SQL Developer attaches to the waiting session-- The blocked CONNECT_TCP call returns, execution continues---- 4. The failing procedure is called — exception breakpoints and line-- breakpoints now work as in local debugging---- Network requirement:-- Database server ---outbound TCP 4000---> SQL Developer workstation-- (database server initiates; SQL Developer listens)-- ===================================================-- Step 1: In SQL Developer, start the remote debug listener-- Run > Remote Debug > Listening Port: 4000 > OK-- Status bar shows: "Waiting for remote debug connection..."-- ===================================================-- ===================================================-- Step 2: In the TARGET database session, run this block-- Replace the host with your SQL Developer machine IP or hostname-- ===================================================DECLARE
v_batch_id NUMBER := 12345;
v_run_date DATE := DATE'2026-04-14';
BEGIN-- Initiate debug connection from database to SQL Developer-- This BLOCKS until SQL Developer attaches (or timeout expires)
DBMS_DEBUG_JDWP.CONNECT_TCP(
host => '10.0.1.45', -- SQL Developer host IP or hostname
port => 4000, -- must match SQL Developer listening port
timeout => 60-- seconds to wait before giving up
);
-- CONNECT_TCP returned — SQL Developer is now attached-- Breakpoints are now active. Call the failing procedure:
io.thecodeforge.payment.reconcile_batch(
p_batch_id => v_batch_id,
p_run_date => v_run_date
);
-- Optionally disconnect after debugging is complete-- DBMS_DEBUG_JDWP.DISCONNECT;END;
/
-- ===================================================-- Verify JDWP privilege (required on Oracle 12c+)-- ===================================================SELECT grantee, privilege
FROM dba_sys_privs
WHERE grantee = 'YOUR_USER'AND privilege LIKE'%JDWP%';
-- Grant if missing (run as DBA)GRANTJDWPTO your_developer_user;
-- ===================================================-- Troubleshooting: test port reachability from DB server-- (run from OS on the database server, not from SQL*Plus)-- ===================================================-- Linux/Unix:-- nc -zv your-sqldeveloper-host.example.com 4000-- telnet your-sqldeveloper-host.example.com 4000---- Windows (PowerShell):-- Test-NetConnection -ComputerName your-sqldeveloper-host -Port 4000-- ===================================================-- Defensive wrapper: attempt debug attach, fall back gracefully-- Useful in test automation where debugger attachment is optional-- ===================================================CREATEORREPLACEPROCEDURE io.thecodeforge.debug.try_attach(
p_debug_host INVARCHAR2DEFAULT'localhost',
p_debug_port INNUMBERDEFAULT4000,
p_timeout INNUMBERDEFAULT15
) ISBEGIN
DBMS_DEBUG_JDWP.CONNECT_TCP(
host => p_debug_host,
port => p_debug_port,
timeout => p_timeout
);
DBMS_OUTPUT.PUT_LINE(
'Debug session attached: ' || p_debug_host || ':' || p_debug_port
);
EXCEPTIONWHENOTHERSTHEN-- Timeout or connection refused — continue without debugger
DBMS_OUTPUT.PUT_LINE(
'Debug attach failed (' || SQLERRM || ') — running without debugger'
);
END try_attach;
/
Remote Debugging — Operational Rules
Remote debugging adds 200–500 ms latency per step — acceptable for diagnosis, not for step-by-step code review of 500-line procedures
The database server initiates the outbound TCP connection to SQL Developer — firewall rules must allow outbound traffic from the DB server to your workstation on port 4000
DBMS_DEBUG_JDWP.CONNECT_TCP blocks until SQL Developer attaches — if SQL Developer is not already listening, the call times out after the specified seconds
On Oracle 12c+, the user also needs JDWP privilege (separate from DEBUG CONNECT SESSION): GRANT JDWP TO user
Never enable remote debugging against a production database — the debug connection holds open a session, adds latency, and requires DEBUG compilation which should not exist in production
If you need to debug a staging environment shared with QA, coordinate your debug session timing to avoid interfering with test runs that depend on consistent procedure behavior
Production Insight
Remote debugging is the right tool for environment-specific errors — the ones that occur in staging but not locally because staging has production-schema data volume or specific configuration.
The 200–500 ms per-step overhead is real but manageable. For most ORA-06512 debugging sessions, you need 10–20 steps at most. That is 5–10 seconds of overhead, not hours.
If the latency is consistently over 1 second per step, the network path between the database server and your workstation has a problem that needs to be addressed separately from the debugging session.
Key Takeaway
Remote debugging connects SQL Developer to a database session on a different host — the database initiates the outbound TCP connection, not SQL Developer.
Pre-configure firewall rules before your next incident — discovering the debug port is blocked during a 3 AM incident adds frustration without adding diagnostic value.
Never use remote debugging on production databases — use enhanced FORMAT_CALL_STACK logging instead.
Debugging Environment Selection
IfError is reproducible locally with test data in your dev schema
→
UseUse local SQL Developer connection — no CONNECT_TCP needed, fastest iteration, zero network latency
IfError occurs only in test or staging — requires staging data volume or configuration
→
UseUse remote debugging with DBMS_DEBUG_JDWP.CONNECT_TCP. Configure the firewall rule in advance — do not wait until the incident to discover the port is blocked.
IfError is intermittent and cannot be reproduced on demand
→
UseSet exception breakpoints, leave the debug session running, and execute the batch or workload repeatedly until the breakpoint fires. Remote sessions can remain open for hours.
IfError occurs in a long-running batch job that takes 40+ minutes to reach the failing code
→
UseSet a conditional exception breakpoint to filter for the specific data condition that triggers the failure. This avoids stepping through thousands of successful iterations.
IfError occurs in production only and cannot be reproduced in staging
→
UseDo not use the debugger. Use enhanced error logging with FORMAT_CALL_STACK, FORMAT_ERROR_BACKTRACE, and parameter capture. Reproduce in staging using logged parameters.
Automated Stack Capture Without the Debugger
Interactive debugging is not always available. Production environments should never have SQL Developer attached — the performance impact, security exposure of DEBUG compilation, and operational risk are not justified for production debugging. Scheduled batch jobs run unattended without a developer waiting to attach a debug session. And intermittent errors that require hours of waiting for conditions to align are impractical to debug interactively.
For all of these scenarios, the fallback is programmatic stack capture using DBMS_UTILITY.FORMAT_CALL_STACK and DBMS_UTILITY.FORMAT_ERROR_STACK. These two functions together capture the same information as the debugger's Call Stack and error display — the call chain leading to the error, the exact error code and message, and the ORA-06512 frame lines. Capturing them in an AUTONOMOUS_TRANSACTION ensures the log record survives even when the main transaction rolls back due to the error.
The critical addition over basic ORA-06512 logging is the parameter capture: log the input values that were active at the time of the error alongside the stack. A stack trace without parameters tells you where the error occurred. Parameters tell you why — and enable exact reproduction in a test environment where you can attach the debugger.
automated_stack_capture.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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
-- ===================================================-- Error log table: production stack capture schema-- ===================================================CREATETABLE io.thecodeforge.logging.error_log (
error_id NUMBERGENERATEDALWAYSASIDENTITYPRIMARYKEY,
module_name VARCHAR2(128) NOTNULL,
error_code NUMBER,
error_message CLOB,
call_stack CLOB, -- DBMS_UTILITY.FORMAT_CALL_STACK
error_stack CLOB, -- DBMS_UTILITY.FORMAT_ERROR_STACK
error_backtrace CLOB, -- DBMS_UTILITY.FORMAT_ERROR_BACKTRACE
parameters CLOB, -- input values at time of error
session_id NUMBER,
os_user VARCHAR2(128),
db_user VARCHAR2(128),
client_info VARCHAR2(64),
module_ctx VARCHAR2(64),
action_ctx VARCHAR2(64),
created_at TIMESTAMPDEFAULTSYSTIMESTAMP
);
-- Index for common query patternsCREATEINDEX io.thecodeforge.logging.err_log_code_ts_idx
ON io.thecodeforge.logging.error_log (error_code, created_at DESC);
CREATEINDEX io.thecodeforge.logging.err_log_module_ts_idx
ON io.thecodeforge.logging.error_log (module_name, created_at DESC);
-- ===================================================-- Centralized error capture procedure-- Call this from EVERY exception handler before re-raising-- ===================================================CREATEORREPLACEPROCEDURE io.thecodeforge.logging.capture_error(
p_module INVARCHAR2,
p_parameters INCLOBDEFAULTNULL
) ISPRAGMA AUTONOMOUS_TRANSACTION; -- survives main transaction rollbackBEGININSERTINTO io.thecodeforge.logging.error_log (
module_name,
error_code,
error_message,
call_stack,
error_stack,
error_backtrace,
parameters,
session_id,
os_user,
db_user,
client_info,
module_ctx,
action_ctx,
created_at
) VALUES (
p_module,
SQLCODE,
SQLERRM,
DBMS_UTILITY.FORMAT_CALL_STACK, -- who called whom at error time
DBMS_UTILITY.FORMAT_ERROR_STACK, -- ORA codes and messages (up to 2000 bytes)
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE, -- original line numbers preserved through re-raises
p_parameters,
SYS_CONTEXT('USERENV', 'SESSIONID'),
SYS_CONTEXT('USERENV', 'OS_USER'),
SYS_CONTEXT('USERENV', 'SESSION_USER'),
SYS_CONTEXT('USERENV', 'CLIENT_INFO'),
SYS_CONTEXT('USERENV', 'MODULE'),
SYS_CONTEXT('USERENV', 'ACTION'),
SYSTIMESTAMP
);
COMMIT; -- autonomous transaction commit — does not affect caller's transactionEXCEPTIONWHENOTHERSTHEN-- If error logging itself fails, write to alert log and continue-- Never let logging failure suppress the original errorSYS.DBMS_SYSTEM.KSDWRT(
2, -- alert log destination'ERROR LOG FAILED in capture_error for module=' || p_module ||
' SQLCODE=' || SQLCODE
);
END capture_error;
/
-- ===================================================-- Usage pattern: every production exception handler-- ===================================================CREATEORREPLACEPROCEDURE io.thecodeforge.payment.reconcile_batch(
p_batch_id INNUMBER,
p_run_date INDATE
) IS
v_processed NUMBER := 0;
v_failed NUMBER := 0;
BEGIN-- Business logicFOR rec IN (
SELECT * FROM io.thecodeforge.payment.pending_transactions
WHERE batch_id = p_batch_id
) LOOPBEGIN
io.thecodeforge.payment.process_transaction(
p_txn_id => rec.transaction_id,
p_batch_id => p_batch_id
);
v_processed := v_processed + 1;
EXCEPTIONWHENOTHERSTHEN-- Capture: stack + parameters + context
io.thecodeforge.logging.capture_error(
p_module => 'RECONCILE_BATCH.LOOP',
p_parameters =>
'batch_id=' || p_batch_id || CHR(10) ||
'run_date=' || TO_CHAR(p_run_date, 'YYYY-MM-DD') || CHR(10) ||
'txn_id=' || rec.transaction_id || CHR(10) ||
'processed_so_far=' || v_processed
);
v_failed := v_failed + 1;
-- For batch processing: log-and-continue pattern-- Do NOT re-raise here — allows batch to process remaining recordsEND;
ENDLOOP;
-- Report completion with counts
io.thecodeforge.logging.capture_error(
p_module => 'RECONCILE_BATCH.SUMMARY',
p_parameters =>
'batch_id=' || p_batch_id || CHR(10) ||
'processed=' || v_processed || CHR(10) ||
'failed=' || v_failed
);
COMMIT;
END reconcile_batch;
/
-- ===================================================-- Post-mortem query: find the failing call chain-- Run this after a production error to reconstruct the stack-- ===================================================SELECT
el.error_id,
el.module_name,
el.error_code,
el.error_message,
el.parameters,
el.call_stack,
el.error_backtrace,
el.db_user,
el.os_user,
el.created_at
FROM io.thecodeforge.logging.error_log el
WHERE el.created_at > SYSTIMESTAMP - INTERVAL'1'HOURAND el.error_code != 0-- exclude informational entriesORDERBY el.created_at DESCFETCHFIRST20ROWSONLY;
Production Stack Capture — Three Functions, One Complete Picture
FORMAT_CALL_STACK: shows the procedure call chain at the moment capture_error was called — equivalent to the debugger's Call Stack window
FORMAT_ERROR_STACK: shows the ORA error codes and messages including ORA-06512 lines — up to 2000 bytes (use when SQLERRM's 512-byte limit truncates deep stacks)
FORMAT_ERROR_BACKTRACE: shows the original line number where the error was first raised, preserved through re-raises — this is the one that gives you the exact line even when multiple RAISE statements are in the propagation path
Log all three, always — each provides information the others do not
AUTONOMOUS_TRANSACTION is non-negotiable for error logging — without it, the ROLLBACK triggered by the error deletes your log record
Log input parameters in a structured format (key=value separated by CHR(10)) so you can parse them programmatically and reproduce the exact failure condition in a test environment
Production Insight
FORMAT_CALL_STACK and FORMAT_ERROR_STACK together give you everything the debugger's Call Stack window shows — the same call chain, the same error codes, the same line numbers. The difference is that you see it after the fact instead of interactively.
AUTONOMOUS_TRANSACTION is the most important implementation detail in capture_error. Without it, the INSERT to error_log is part of the main transaction, and when the error causes a ROLLBACK, your log record disappears with it. This produces the nightmare scenario: an error occurs, your handler logs it, the transaction rolls back, and the log is empty — no trace that the error ever happened.
Parameter logging enables exact failure reproduction. A stack trace without parameters tells you which SELECT INTO failed. Parameters tell you which customer_id, which report_type, which batch_id caused it — and those values let you reproduce it in a test environment where you can attach the debugger.
Key Takeaway
For production: use FORMAT_CALL_STACK + FORMAT_ERROR_STACK + FORMAT_ERROR_BACKTRACE in an AUTONOMOUS_TRANSACTION logging procedure.
Log all three functions — each captures information the others do not.
Always include input parameter values in the log — a stack without parameters cannot be reproduced for test environment debugging.
AUTONOMOUS_TRANSACTION is non-negotiable — without it, ROLLBACK erases your error log.
● Production incidentPOST-MORTEMseverity: high
Three-Day ORA-06512 Diagnosis Eliminated by Debugger Setup
Symptom
Nightly batch job fails at 02:34 UTC with ORA-01403 followed by three ORA-06512 lines pointing to nested procedures: io.thecodeforge.report.generator (line 142), io.thecodeforge.report.batch_runner (line 87), and io.thecodeforge.scheduler.nightly_job (line 23). The batch processes 47 report types and the error only fires for one of them. Manual stack trace analysis required identifying which of the 47 report types triggered the failure, then reading the source at the correct version, then correlating line 142 — which shifted every time someone edited the package for an unrelated fix. Each debugging cycle required redeployment and re-execution of the full batch, which takes 40 minutes to reach the failing report type.
Assumption
The team believed SQL Developer was primarily a query and script tool. They had never used its PL/SQL debugger and assumed debugging PL/SQL meant using DBMS_OUTPUT.PUT_LINE to print variable values. Three engineers rotated through 72 hours of manual debugging cycles, each one adding more PUT_LINE statements and re-running the batch.
Root cause
A SELECT INTO in io.thecodeforge.report.generator was returning no rows for one specific report_type value: 'QUARTERLY_ADJUSTMENT'. This report type was added to the scheduler six weeks earlier but the corresponding template record was never inserted into io.thecodeforge.report.templates. The error propagated through three nested calls. The NULL p_report_type check that should have caught this was present in the batch_runner but was only applied when p_report_type was NULL — not when it was an unrecognized non-NULL value. The debugger exception breakpoint would have halted at the exact SELECT INTO on the first attempt, showing p_report_type = 'QUARTERLY_ADJUSTMENT' and v_count = 0 in the Variables window. Instead, 72 hours of manual work produced the same answer.
Fix
1. Configured SQL Developer remote debugging connection to the test database — 20 minutes total including privilege grants and firewall rule
2. Set exception breakpoint for ORA-01403 (NO_DATA_FOUND) in the Breakpoints window
3. Executed batch job through the debugger with p_run_date matching the failing night — execution halted at the SELECT INTO in io.thecodeforge.report.generator in 4 minutes
4. Variables window showed p_report_type = 'QUARTERLY_ADJUSTMENT' and confirmed zero rows in the templates table for that value
5. Root cause confirmed: missing template record. Inserted the missing row into io.thecodeforge.report.templates
6. Added a pre-execution validation check in batch_runner to verify all report types have corresponding template records before starting the batch run — fail fast with a clear error instead of propagating ORA-01403 from deep in the call chain
Key lesson
Configure SQL Developer debugger before starting any manual stack trace parsing — the 20-minute setup cost is recovered on the first debugging session
Exception breakpoints eliminate manual ORA-06512 line number correlation entirely — you do not need to know which line to break on
Remote debugging works against test databases without changing application code — DBMS_DEBUG_JDWP.CONNECT_TCP is the only addition needed
The root cause of this incident was a missing reference data record, but the debugging method made it a 3-day investigation instead of a 4-minute one
Add pre-execution validation at batch entry points to catch configuration errors before they propagate through nested call chains
Production debug guideSymptom-to-action mapping for common debugger setup and runtime issues7 entries
Symptom · 01
Debugger does not stop at breakpoints — execution runs to completion without pausing
→
Fix
The most common cause is missing DEBUG compilation. Run: ALTER PROCEDURE owner.proc_name COMPILE DEBUG; for each procedure in the call chain. Verify with: SELECT name, type, CASE WHEN debuginfo = 'YES' THEN 'DEBUG OK' ELSE 'MISSING DEBUG' END AS debug_status FROM user_procedures WHERE name = 'PROC_NAME'. Also check that you have DEBUG CONNECT SESSION privilege and that the Breakpoints window shows the breakpoint as active (green check), not disabled (grey).
Symptom · 02
Exception breakpoint fires but Call Stack window shows only 'anonymous block' with no procedure names
→
Fix
The error occurred in dynamic SQL executed via EXECUTE IMMEDIATE, in a trigger, or in a procedure compiled without DEBUG that is not visible to the debugger. For dynamic SQL: set a regular line breakpoint before the EXECUTE IMMEDIATE and check v_sql variable content. For triggers: set a breakpoint inside the trigger body, then perform the triggering DML. For procedures compiled without DEBUG: recompile the specific missing procedure with ALTER PROCEDURE proc_name COMPILE DEBUG.
Symptom · 03
Remote debugging connection refused — SQL Developer shows 'Connection refused' when trying to attach
→
Fix
Work through this checklist in order: (1) Confirm DBMS_DEBUG_JDWP.CONNECT_TCP was called from the target session before SQL Developer tried to attach — the call blocks and waits; (2) Verify the port number matches between the CONNECT_TCP call and SQL Developer preferences (default 4000); (3) Check firewall rules — the database server must allow outbound TCP to the SQL Developer host on the debug port; (4) Confirm the SQL Developer host IP is reachable from the database server, not just the other way around; (5) On Oracle 12c+, confirm the user has JDWP privilege: SELECT * FROM dba_sys_privs WHERE privilege LIKE '%JDWP%'.
Symptom · 04
Variables show <unreadable> or <value not available> in the watch window
→
Fix
The procedure was compiled without the DEBUG flag. Run ALTER PROCEDURE proc_name COMPILE DEBUG and restart the debug session. If the variable shows <unreadable> even after DEBUG compilation, it may be a package global — package globals are only visible when the Call Stack frame is within the package body. Navigate to that frame in the Call Stack window to make the variable accessible.
Symptom · 05
Debugger is extremely slow — each step takes 2–10 seconds instead of being near-instant
→
Fix
Remote debugging over high-latency networks adds 200–500 ms per step under normal conditions; high-latency or lossy networks amplify this significantly. Try: (1) reduce the number of expressions in the Watch window — each watch expression is evaluated on every step; (2) disable automatic expression evaluation in SQL Developer preferences; (3) use Step Over instead of Step Into when possible to skip into library code you do not need to debug; (4) if the latency is consistently over 1 second, switch to a local connection or use the DBMS_OUTPUT logging approach instead.
Symptom · 06
Debug session starts but then the procedure raises ORA-01031 insufficient privileges on the first line
→
Fix
The DEBUG privilege is granted but the procedure owner lacks execute privilege on the objects it references, or the session user lacks DEBUG ON the specific package. Check: SELECT * FROM dba_tab_privs WHERE grantee = 'YOUR_USER' AND privilege = 'DEBUG'. Grant specifically: GRANT DEBUG ON owner.package_name TO developer_user. For owned objects, GRANT DEBUG CONNECT SESSION is sufficient — DEBUG ANY PROCEDURE is only needed for objects in other schemas.
Symptom · 07
Breakpoint fires in wrong procedure — not the one expected
→
Fix
Multiple compiled versions of the same procedure name exist in different schemas, and the debugger is attaching to the wrong one. Check: SELECT owner, object_name, object_type, status FROM dba_objects WHERE object_name = 'PROC_NAME' ORDER BY owner. Ensure the connection in SQL Developer uses the correct schema owner. If the procedure exists in multiple schemas, fully qualify it in your test execution block.
★ SQL Developer Debugger Quick SetupCopy-paste ready commands for debugger configuration, privilege grants, and DEBUG compilation — run these before starting any debug session
Need to enable debugging for a specific package or procedure−
Immediate action
Grant privileges and compile with DEBUG flag
Commands
-- Grant required privileges (run as DBA)
GRANT DEBUG CONNECT SESSION TO your_developer_user;
GRANT DEBUG ANY PROCEDURE TO your_developer_user;
-- Alternative: grant DEBUG on specific objects only (least-privilege approach)
GRANT DEBUG ON io.thecodeforge.report.generator TO your_developer_user;
GRANT DEBUG ON io.thecodeforge.report.batch_runner TO your_developer_user;
GRANT DEBUG ON io.thecodeforge.scheduler.nightly_job TO your_developer_user;
-- Compile procedures with DEBUG flag
ALTER PROCEDURE io.thecodeforge.payment.reconcile_batch COMPILE DEBUG;
ALTER PACKAGE io.thecodeforge.report.generator COMPILE DEBUG;
ALTER PACKAGE io.thecodeforge.report.generator COMPILE BODY DEBUG;
-- Verify DEBUG compilation status
SELECT name, type,
CASE WHEN debuginfo = 'YES' THEN 'DEBUG ENABLED'
ELSE 'NO DEBUG INFO — recompile with DEBUG' END AS debug_status
FROM user_procedures
WHERE name IN ('RECONCILE_BATCH', 'GENERATOR', 'BATCH_RUNNER')
ORDER BY name;
Fix now
Once privileges are granted and DEBUG compilation is confirmed, set your exception breakpoint in SQL Developer's Breakpoints window and start the debug session. Do not forget to revert DEBUG compilation before promoting to production.
Need to initiate a remote debug session from the target database+
Immediate action
Run DBMS_DEBUG_JDWP.CONNECT_TCP from the target session before executing the failing code
Commands
-- Run this in the TARGET database session
-- This blocks until SQL Developer attaches (or timeout expires)
BEGIN
DBMS_DEBUG_JDWP.CONNECT_TCP(
host => 'your-sqldeveloper-host.example.com', -- SQL Developer machine IP
port => 4000, -- must match SQL Developer preferences
timeout => 30 -- seconds to wait for attachment
);
-- After SQL Developer attaches, execution continues here
-- Place the failing call immediately after CONNECT_TCP
io.thecodeforge.payment.reconcile_batch(
p_batch_id => 12345,
p_run_date => DATE '2026-04-14'
);
END;
/
-- In SQL Developer: Run > Debug > (select your connection)
-- SQL Developer will attach to the waiting session
-- Then set exception breakpoints in the Breakpoints window:
-- Breakpoints window > right-click > Add Exception Breakpoint
-- Error number: 1403 (for ORA-01403 NO_DATA_FOUND)
-- Error number: 1 (for ORA-00001 unique constraint violated)
-- Error number: 6502 (for ORA-06502 VALUE_ERROR)
-- Verify the debug port is open and reachable from the DB server
-- Run this from the database server OS (not SQL*Plus):
-- telnet your-sqldeveloper-host.example.com 4000
Fix now
If CONNECT_TCP times out, SQL Developer was not yet listening when the call was made. Start the SQL Developer debug listener first (Run > Debug > connection), then call CONNECT_TCP. The listener must be active before the database session initiates the connection.
Need to verify which objects in a call chain are missing DEBUG compilation+
Immediate action
Query user_procedures across the full call chain before starting the debug session
Commands
-- Check DEBUG status for all objects in a known call chain
SELECT p.object_name, p.object_type, p.procedure_name,
CASE WHEN p.debuginfo = 'YES' THEN 'DEBUG OK'
ELSE 'NEEDS: ALTER ' || p.object_type || ' ' ||
p.object_name || ' COMPILE DEBUG;' END AS action_needed
FROM user_procedures p
WHERE p.object_name IN (
'RECONCILE_BATCH', 'GENERATOR', 'BATCH_RUNNER', 'NIGHTLY_JOB'
)
ORDER BY p.object_name, p.procedure_name;
-- Find all procedures that reference a specific object
-- (useful for identifying the full call chain before debugging)
SELECT DISTINCT d.name AS dependent_object, d.type AS dependent_type,
d.referenced_name AS calls, d.referenced_type
FROM user_dependencies d
WHERE d.referenced_name = 'REPORT_TEMPLATES'
AND d.referenced_type IN ('TABLE','VIEW')
ORDER BY d.name;
-- Then revert non-debug objects after the session
-- ALTER PACKAGE io.thecodeforge.report.generator COMPILE;
-- ALTER PACKAGE io.thecodeforge.report.generator COMPILE BODY;
Fix now
Any object in the call chain that shows 'NEEDS RECOMPILE' must be recompiled with DEBUG before starting the session. Breakpoints in that object will be silently ignored otherwise.
Debugging Approaches for ORA-06512
Approach
Time to Diagnosis
Environment
Context Quality
Best For
Manual ORA-06512 text parsing
Slow (30–90 min per cycle)
Any
Low — line numbers only; line numbers shift with code changes
Single-procedure scripts with no nested calls; debugging on a machine where SQL Developer is unavailable
DBMS_OUTPUT.PUT_LINE logging
Slow (15–30 min per cycle)
Any with output enabled
Low to Medium — only what you remembered to log; requires redeploy per iteration
Legacy codebases without a modern error log table; quick smoke-check of a known variable value
Development environment; errors reproducible with local test data; any new ORA-06512 investigation
SQL Developer remote debugger
Medium (5–15 min setup + 2–5 min diagnosis)
Test/Staging
Highest — identical to local, with 200–500 ms per-step overhead
Environment-specific errors that require staging data volume or configuration
Exception breakpoints (any connection)
Fastest (under 3 min from breakpoint set to root cause)
Local or Remote
Highest — halts at the exact raising line with full pre-handler variable state
Known error code investigation; eliminating manual line-number correlation entirely
FORMAT_CALL_STACK + error log table
Fast (post-mortem, under 5 min to read the log)
Production or any automated job
Medium — stack text and parameters but no interactive variable inspection
Production errors; unattended batch jobs; intermittent errors captured on first occurrence
Conditional breakpoints
Medium setup, then fastest (fires only on matching data)
Local or Remote
Highest — full interactive state, filtered to the specific data condition
Intermittent errors tied to specific parameter values; batch jobs with millions of successful iterations before the error
Key takeaways
1
Exception breakpoints halt execution at the exact line that raises the error
eliminating manual ORA-06512 line number parsing entirely and reducing diagnosis time from 30–90 minutes to under 5 minutes
2
Both conditions must be true before debugging works
correct privilege grants AND DEBUG compilation of every procedure in the call chain — missing either produces silent failure with no error message
3
The Call Stack window replaces textual ORA-06512 lines with a clickable navigable view
click any frame to jump to that source line and inspect that scope's variables
4
Remote debugging architecture
the database session initiates outbound TCP to SQL Developer, not the reverse — SQL Developer must be listening before DBMS_DEBUG_JDWP.CONNECT_TCP is called
5
In production, use FORMAT_CALL_STACK + FORMAT_ERROR_STACK + FORMAT_ERROR_BACKTRACE in an AUTONOMOUS_TRANSACTION logging procedure
log input parameters alongside the stack to enable exact failure reproduction in a test environment
6
Always revert DEBUG compilation before promoting to production
add a CI/CD gate that checks debuginfo = 'YES' and fails the pipeline if any production-bound object is still in debug mode
Common mistakes to avoid
6 patterns
×
Not compiling procedures with the DEBUG flag before starting a debug session
Symptom
Breakpoints are silently ignored — execution runs to completion without pausing. Variables show as <unreadable> in the Watch window. The debugger appears to be broken or misconfigured, leading developers to abandon it.
Fix
Run ALTER PROCEDURE owner.proc_name COMPILE DEBUG before starting the session. For packages, compile both spec and body: ALTER PACKAGE owner.pkg_name COMPILE DEBUG; ALTER PACKAGE owner.pkg_name COMPILE BODY DEBUG. Verify with: SELECT name, type, CASE WHEN debuginfo = 'YES' THEN 'OK' ELSE 'MISSING' END FROM user_procedures WHERE name = 'YOUR_PROC'. Repeat for every procedure in the call chain — a single non-debug procedure in the middle of the chain breaks the debugger's view of that frame.
×
Reading ORA-06512 stack traces manually instead of using exception breakpoints
Symptom
Engineers spend 30–90 minutes correlating line numbers with source code. Each code change shifts line numbers, invalidating the correlation and requiring re-analysis. Multiple debugging cycles are needed because manual guesses at variable values are wrong. The team treats the debugger as a 'nice to have' rather than a primary diagnostic tool.
Fix
Set an exception breakpoint for the error code shown in the ORA-06512 stack before doing any manual analysis. The 5-minute setup investment (privilege grants + DEBUG compilation + exception breakpoint) is always recovered on the first debugging session. Use the Call Stack window to navigate the procedure chain interactively. The manual approach is always slower for nested procedure chains with three or more levels.
×
Leaving DEBUG compilation enabled on objects that get promoted to production
Symptom
Production objects contain debug symbol information visible in USER_PROCEDURES. Object sizes are 10–15% larger than necessary. Internal variable names, loop counter names, and parameter names are exposed in the data dictionary — a security concern for procedures handling PII, credentials, or business-sensitive logic. If someone accidentally attaches a debug session to production, breakpoints would work.
Fix
Revert to non-debug compilation before promoting: ALTER PROCEDURE proc_name COMPILE; ALTER PACKAGE pkg_name COMPILE; ALTER PACKAGE pkg_name COMPILE BODY. Add a pre-deploy CI/CD gate: SELECT COUNT(*) FROM user_procedures WHERE debuginfo = 'YES' — if this returns any rows, fail the pipeline. Keep debug compilation strictly within developer-owned local schemas.
×
Using remote debugging over a high-latency network without adjusting expectations or technique
Symptom
Each step takes 2–10 seconds instead of being near-instant. Developers conclude the debugger is broken or unusable and abandon it. They return to DBMS_OUTPUT.PUT_LINE loops, which take longer but feel more familiar.
Fix
Accept 200–500 ms per step as normal for remote connections. Reduce Watch window expressions to the minimum needed — each expression is evaluated on every step. Use Step Over (F8) to skip library calls you do not need to inspect. Use exception breakpoints to jump directly to the error location instead of stepping through hundreds of lines. If latency consistently exceeds 1 second, investigate the network path separately.
×
Not logging input parameters alongside error stacks in production
Symptom
Production error log shows the full FORMAT_CALL_STACK and FORMAT_ERROR_STACK — the call chain is clear. But there are no parameter values. Engineers cannot reproduce the failure in a test environment because they do not know which batch_id, customer_id, or report_type caused it. The incident remains unresolved until the same error recurs and a developer catches it with DBMS_OUTPUT.
Fix
Always pass p_parameters to capture_error with all input values serialized: 'batch_id=' || p_batch_id || CHR(10) || 'run_date=' || TO_CHAR(p_run_date, 'YYYY-MM-DD'). These values enable exact failure reproduction in a test environment where you can attach the SQL Developer debugger. Without parameters, the stack trace is interesting but not actionable.
×
Starting a remote debug session without verifying firewall rules in advance
Symptom
DBMS_DEBUG_JDWP.CONNECT_TCP times out after 30–60 seconds. SQL Developer never shows an attachment. The debugging session cannot start. During a production incident, this wastes 30–60 minutes confirming the network path is blocked before switching to the logging fallback.
Fix
Test port reachability before you need to debug in anger: from the database server OS, run: nc -zv your-sqldeveloper-host.example.com 4000 (Linux) or Test-NetConnection -ComputerName your-sqldeveloper-host -Port 4000 (PowerShell). Do this test during normal working hours, not during a 3 AM incident. Document the result and open firewall rules proactively in test environments.
INTERVIEW PREP · PRACTICE MODE
Interview Questions on This Topic
Q01SENIOR
How would you set up SQL Developer to debug an ORA-01403 error that occu...
Q02SENIOR
What is the difference between DBMS_UTILITY.FORMAT_CALL_STACK, FORMAT_ER...
Q03SENIOR
How would you debug an ORA-06512 error in production where you cannot at...
Q04JUNIOR
What privileges are required for SQL Developer debugging and why might b...
Q05SENIOR
Explain the architecture of SQL Developer remote debugging — which side ...
Q01 of 05SENIOR
How would you set up SQL Developer to debug an ORA-01403 error that occurs at line 142 in a three-level nested procedure call chain?
ANSWER
Start by compiling all three procedures in the call chain with DEBUG: ALTER PROCEDURE proc_name COMPILE DEBUG for standalone procedures, ALTER PACKAGE pkg_name COMPILE BODY DEBUG for package procedures. Verify with SELECT name, debuginfo FROM user_procedures WHERE name IN (...). Grant DEBUG CONNECT SESSION and DEBUG ANY PROCEDURE to the developer account if not already granted. In SQL Developer, open the Breakpoints window and add an exception breakpoint for error number 1403. Execute the failing call through the debugger — either locally or via DBMS_DEBUG_JDWP.CONNECT_TCP for remote. When the exception fires, the debugger halts at the exact SELECT INTO at line 142. Inspect the Variables window: the NULL parameter or the unrecognized value that caused the empty result set will be immediately visible. Click Frame 1 in the Call Stack window to see what the caller passed. Root cause is typically identified in under 5 minutes.
Q02 of 05SENIOR
What is the difference between DBMS_UTILITY.FORMAT_CALL_STACK, FORMAT_ERROR_STACK, and FORMAT_ERROR_BACKTRACE, and when would you log each one?
ANSWER
They capture three different views of the same error event. FORMAT_CALL_STACK returns the current procedure call chain at the moment the function is called — it shows who called whom and at which line, equivalent to the debugger's Call Stack window. FORMAT_ERROR_STACK returns the ORA error codes and messages including all ORA-06512 lines — it is the textual error stack up to 2000 bytes, longer than SQLERRM's 512-byte limit. FORMAT_ERROR_BACKTRACE returns the original line number where the exception was first raised, preserved through RAISE statements — this is the one that does not shift when you re-raise an exception through multiple handlers. The best practice is to log all three: FORMAT_CALL_STACK for the call chain, FORMAT_ERROR_STACK for the error codes, FORMAT_ERROR_BACKTRACE for the original line number. They are complementary, not redundant.
Q03 of 05SENIOR
How would you debug an ORA-06512 error in production where you cannot attach SQL Developer's interactive debugger?
ANSWER
Implement centralized error logging using an AUTONOMOUS_TRANSACTION procedure that captures FORMAT_CALL_STACK, FORMAT_ERROR_STACK, FORMAT_ERROR_BACKTRACE, input parameters, and session context. The AUTONOMOUS_TRANSACTION is critical — without it, the ROLLBACK triggered by the error deletes the log record. Query the error_log table post-incident to reconstruct the full call chain and parameter values. Take the logged parameter values and reproduce the exact failure in a test or staging environment where you CAN attach SQL Developer. Set an exception breakpoint for the error code, pass the logged parameters to the failing procedure, and let the debugger find the line. The production log provides the parameters; the test environment provides the interactivity.
Q04 of 05JUNIOR
What privileges are required for SQL Developer debugging and why might breakpoints still not work even after granting them?
ANSWER
Two system privileges are required: DEBUG CONNECT SESSION allows the session to initiate a debug connection, and DEBUG ANY PROCEDURE allows setting breakpoints in compiled objects across all schemas. For least-privilege setups, GRANT DEBUG ON specific_package TO developer replaces DEBUG ANY PROCEDURE for specific objects. On Oracle 12c+, the JDWP privilege is also needed for remote debugging via DBMS_DEBUG_JDWP. Even with all privileges granted, breakpoints silently fail if the procedure was not compiled with the DEBUG flag. The two conditions are independent — privileges control what you are allowed to debug, DEBUG compilation controls whether the compiled object contains the symbol table information the debugger needs to bind breakpoints. Missing either one produces silent failure. Verify both: check privileges in dba_sys_privs and debug status in user_procedures WHERE debuginfo = 'YES'.
Q05 of 05SENIOR
Explain the architecture of SQL Developer remote debugging — which side initiates the TCP connection and why does the firewall direction matter?
ANSWER
The database session initiates the outbound TCP connection to SQL Developer using DBMS_DEBUG_JDWP.CONNECT_TCP. SQL Developer listens on a configurable port (default 4000) and accepts the incoming connection. This is counterintuitive — you might expect the debugger tool to connect to the database, but it is the opposite. The firewall implication is that the database server must be able to make outbound TCP connections to the SQL Developer workstation on the debug port. Many corporate environments allow outbound TCP from database servers by default, which makes this architecture firewall-friendly. The CONNECT_TCP call blocks until SQL Developer attaches or the timeout expires — so SQL Developer must be listening before CONNECT_TCP is called, not after. The common mistake is starting the debug session in SQL Developer after CONNECT_TCP has already timed out, then wondering why nothing is attached.
01
How would you set up SQL Developer to debug an ORA-01403 error that occurs at line 142 in a three-level nested procedure call chain?
SENIOR
02
What is the difference between DBMS_UTILITY.FORMAT_CALL_STACK, FORMAT_ERROR_STACK, and FORMAT_ERROR_BACKTRACE, and when would you log each one?
SENIOR
03
How would you debug an ORA-06512 error in production where you cannot attach SQL Developer's interactive debugger?
SENIOR
04
What privileges are required for SQL Developer debugging and why might breakpoints still not work even after granting them?
JUNIOR
05
Explain the architecture of SQL Developer remote debugging — which side initiates the TCP connection and why does the firewall direction matter?
SENIOR
FAQ · 6 QUESTIONS
Frequently Asked Questions
01
Can I use SQL Developer debugger against a production database?
You should not, for three reasons: (1) DEBUG compilation must be active for breakpoints to work, and debug objects should not exist in production — they expose internal variable names and are larger than necessary; (2) the JDWP debug connection holds an open session and adds latency to every step, which affects production workloads; (3) the risk of accidentally modifying data or leaving the session in a mid-transaction state during debugging is not acceptable in production. Instead, implement enhanced error logging using DBMS_UTILITY.FORMAT_CALL_STACK, FORMAT_ERROR_STACK, and FORMAT_ERROR_BACKTRACE in AUTONOMOUS_TRANSACTION blocks. Use the logged parameters to reproduce the issue in a staging environment where you can attach the debugger safely.
Was this helpful?
02
Why does my breakpoint not trigger even though the procedure is compiled with DEBUG?
Work through this checklist: (1) Verify the compiled version matches your current source — if you edited the procedure after compiling with DEBUG, recompile: ALTER PROCEDURE proc_name COMPILE DEBUG; (2) Confirm the connection in SQL Developer uses the correct schema owner — a breakpoint set in SCHEMA_A.PROC will not fire when SCHEMA_B.PROC executes, even if they have identical source; (3) Verify you have DEBUG privilege on the object: SELECT * FROM dba_tab_privs WHERE grantee = 'YOUR_USER' AND privilege = 'DEBUG'; (4) For remote debugging, confirm DBMS_DEBUG_JDWP.CONNECT_TCP was called before the procedure executed and SQL Developer had already started listening; (5) Check the Breakpoints window — the breakpoint must show as active (enabled checkbox) and not disabled or skipped.
Was this helpful?
03
How do I debug a trigger that raises ORA-06512?
Triggers are debuggable but require a different approach. First, compile the trigger with DEBUG: ALTER TRIGGER trigger_name COMPILE DEBUG. Set a line breakpoint inside the trigger body at the line you want to inspect, or set an exception breakpoint for the expected error code. Then perform the DML operation that fires the trigger (INSERT, UPDATE, or DELETE) through the debug session. The debugger halts when the trigger executes. The Call Stack window will show the trigger as a frame in the stack. One limitation: the Call Stack context for triggers is less detailed than for standalone procedures — the triggering DML is shown as the parent frame but without parameter details. Use the Variables window to inspect trigger-level variables including :NEW and :OLD record values.
Was this helpful?
04
What is the performance impact of DEBUG compilation in production?
DEBUG compilation increases object size by 10–15% due to embedded symbol tables that map bytecode positions to source lines and variable names. Runtime execution performance when no debugger is attached is identical to non-debug compilation — the symbol table is only read when a debugger connects and requests information. The overhead only appears during an active debug session when breakpoints are hit: each breakpoint hit requires the debugger to evaluate the symbol table and transfer variable values over the JDWP connection, which takes 200–500 ms per hit in remote sessions. None of this overhead applies in production unless someone accidentally attaches a debug session to a production database — which is why you should never have DEBUG compilation in production regardless of the performance argument.
Was this helpful?
05
Can I set a conditional breakpoint that only fires for specific parameter values?
Yes, and this is the correct approach for intermittent errors that occur only under specific data conditions. In the Breakpoints window, right-click a breakpoint and select Properties. In the Condition field, enter a PL/SQL boolean expression such as: p_customer_id IS NULL, p_report_type = 'QUARTERLY_ADJUSTMENT', or v_count = 0. The breakpoint fires only when the condition evaluates to TRUE. Conditions are evaluated at the breakpoint location — variables must be in scope. For exception breakpoints, the condition is evaluated at the moment the exception is raised, using the variables in scope at that point. Use conditional breakpoints for batch jobs that process millions of records successfully before hitting the one record that triggers the error — without the condition, the debugger would halt on every matching exception, making the session unusable.
Was this helpful?
06
How long does DBMS_DEBUG_JDWP.CONNECT_TCP block if SQL Developer is not listening?
CONNECT_TCP blocks for the number of seconds specified in the timeout parameter. The default timeout is implementation-dependent but typically 30–60 seconds if not specified. After the timeout expires, CONNECT_TCP raises an exception (ORA-30671 or a timeout-related error) and execution continues. The defensive wrapper pattern in this guide catches this exception and continues execution without the debugger attached — useful in test automation where debugger attachment is optional. Always specify an explicit timeout: DBMS_DEBUG_JDWP.CONNECT_TCP(host => '...', port => 4000, timeout => 30). A blocked CONNECT_TCP call with no timeout can hold up a database session indefinitely if SQL Developer never attaches.