DEV Community

umzzil nng
umzzil nng

Posted on Originally published at oraerror.com

Oracle ORA-04088 Error: Causes and Solutions Complete Guide

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;
/
Enter fullscreen mode Exit fullscreen mode

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;
/
Enter fullscreen mode Exit fullscreen mode

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;
/
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

Prevention Tips

  1. Always include an EXCEPTION block. Every trigger should catch WHEN OTHERS, log the error to a dedicated log table using PRAGMA AUTONOMOUS_TRANSACTION, and selectively re-raise with RAISE. Never let exceptions propagate silently.

  2. Use %TYPE anchoring and validate inputs before DML. Declare trigger variables using column%TYPE to 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)