ORA-04088: Error During Execution of Trigger — Causes and Fixes
ORA-04088 is a wrapper error that Oracle raises whenever an unhandled exception occurs inside a trigger body during DML or DDL operations. The error itself does not describe the root cause — you must look at the accompanying child errors (e.g., ORA-01400, ORA-04091, ORA-06502) in the error stack to understand what actually went wrong. Any INSERT, UPDATE, or DELETE that fires a failing trigger will have its entire transaction rolled back.
Top 3 Causes
1. Constraint Violations Inside the Trigger
A trigger that performs its own DML (e.g., writing to an audit table) can hit NOT NULL, UNIQUE, or foreign key violations, causing the trigger — and the original DML — to fail.
-- Problematic trigger: inserts NULL into a NOT NULL column
CREATE OR REPLACE TRIGGER trg_audit_bad
AFTER INSERT ON employees
FOR EACH ROW
BEGIN
INSERT INTO audit_log (emp_id, changed_by, change_date)
VALUES (:NEW.employee_id, NULL, SYSDATE); -- ORA-01400: NULL not allowed
END;
/
-- Fixed trigger: use NVL and handle exceptions
CREATE OR REPLACE TRIGGER trg_audit_good
AFTER INSERT ON employees
FOR EACH ROW
BEGIN
INSERT INTO audit_log (emp_id, changed_by, change_date)
VALUES (
:NEW.employee_id,
NVL(SYS_CONTEXT('USERENV', 'SESSION_USER'), 'UNKNOWN'),
SYSDATE
);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
NULL; -- silently ignore duplicate audit entries
WHEN OTHERS THEN
RAISE; -- re-raise unexpected errors
END;
/
2. PL/SQL Runtime Errors (Zero Divide, Value Error, Type Mismatch)
Arithmetic errors, oversized string assignments, or type mismatches inside trigger code raise ORA-06502 or ORA-01476, which bubble up as ORA-04088 if not caught.
-- Problematic trigger: zero-divide risk
CREATE OR REPLACE TRIGGER trg_salary_ratio_bad
BEFORE UPDATE ON employees
FOR EACH ROW
DECLARE
v_ratio NUMBER;
BEGIN
v_ratio := :NEW.salary / :OLD.salary; -- ORA-01476 if OLD.salary = 0
IF v_ratio > 2 THEN
RAISE_APPLICATION_ERROR(-20001, 'Salary increase exceeds 200%.');
END IF;
END;
/
-- Fixed trigger: guard against zero divide
CREATE OR REPLACE TRIGGER trg_salary_ratio_good
BEFORE UPDATE ON employees
FOR EACH ROW
DECLARE
v_ratio NUMBER;
v_old_sal employees.salary%TYPE := NVL(:OLD.salary, 0);
BEGIN
IF v_old_sal = 0 THEN RETURN; END IF; -- skip check if base is zero
v_ratio := :NEW.salary / v_old_sal;
IF v_ratio > 2 THEN
RAISE_APPLICATION_ERROR(-20001,
'Salary increase of ' || ROUND(v_ratio * 100, 1) || '% exceeds 200%.');
END IF;
EXCEPTION
WHEN ZERO_DIVIDE THEN NULL;
WHEN OTHERS THEN RAISE;
END;
/
3. Mutating Table Error (ORA-04091)
When a row-level trigger tries to query or modify the same table that fired it, Oracle raises ORA-04091, which surfaces as ORA-04088. The solution is to use a COMPOUND TRIGGER.
-- Problematic trigger: mutating table
CREATE OR REPLACE TRIGGER trg_mutating_bad
AFTER UPDATE OF salary ON employees
FOR EACH ROW
DECLARE
v_avg NUMBER;
BEGIN
-- Querying the same table being updated → ORA-04091
SELECT AVG(salary) INTO v_avg
FROM employees
WHERE department_id = :NEW.department_id;
END;
/
-- Fixed: COMPOUND TRIGGER collects rows first, checks after statement
CREATE OR REPLACE TRIGGER trg_mutating_good
FOR UPDATE OF salary ON employees
COMPOUND TRIGGER
TYPE t_rec IS RECORD (dept_id NUMBER, new_sal NUMBER);
TYPE t_tab IS TABLE OF t_rec INDEX BY PLS_INTEGER;
g_rows t_tab;
g_idx PLS_INTEGER := 0;
AFTER EACH ROW IS
BEGIN
g_idx := g_idx + 1;
g_rows(g_idx).dept_id := :NEW.department_id;
g_rows(g_idx).new_sal := :NEW.salary;
END AFTER EACH ROW;
AFTER STATEMENT IS
v_avg NUMBER;
BEGIN
FOR i IN 1 .. g_idx LOOP
SELECT AVG(salary) INTO v_avg
FROM employees
WHERE department_id = g_rows(i).dept_id;
IF g_rows(i).new_sal > v_avg * 2 THEN
RAISE_APPLICATION_ERROR(-20002,
'Salary exceeds twice the department average.');
END IF;
END LOOP;
END AFTER STATEMENT;
END trg_mutating_good;
/
Quick Diagnostic Commands
-- Check the full error stack after a failure
SHOW ERRORS TRIGGER your_trigger_name;
-- Find invalid triggers that may cause ORA-04088
SELECT trigger_name, status, table_name
FROM user_triggers
WHERE status != 'ENABLED'
ORDER BY trigger_name;
-- Recompile an invalid trigger
ALTER TRIGGER your_trigger_name COMPILE;
Prevention Tips
Always include an EXCEPTION block. Every trigger should catch
WHEN OTHERS, log the error to a dedicated log table usingPRAGMA AUTONOMOUS_TRANSACTION, and selectively re-raise withRAISE. Never let exceptions propagate silently.Use
%TYPEanchoring and validate inputs before DML. Declare trigger variables usingcolumn%TYPEto avoid type mismatches, always guard against NULL and zero values, and test triggers with boundary-value data in a non-production environment before deployment.
Related Errors
| Error Code | Description |
|---|---|
| ORA-04091 | Mutating table — most common child error of ORA-04088 |
| ORA-06512 | PL/SQL error stack line indicator |
| ORA-04098 | Trigger is invalid and failed re-validation |
| ORA-01400 | Cannot insert NULL — common inside trigger DML |
| ORA-00001 | Unique constraint violated inside trigger |
📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.
Top comments (0)