Skip to content
Home Database ORA-06550: PL/SQL Compilation Error – The Complete Guide

ORA-06550: PL/SQL Compilation Error – The Complete Guide

Where developers are forged. · Structured learning · Free forever.
📍 Part of: PL/SQL → Topic 14 of 27
Complete guide to ORA-06550 — why PL/SQL compilation fails, how to read PLS error codes, how CREATE OR REPLACE silently deploys invalid objects, cascading invalidation through dependency trees, privilege-related compilation failures, and pre-deployment validation patterns for Oracle 19c, 21c, and 23ai.
🧑‍💻 Beginner-friendly — no prior Database experience needed
In this tutorial, you'll learn
Complete guide to ORA-06550 — why PL/SQL compilation fails, how to read PLS error codes, how CREATE OR REPLACE silently deploys invalid objects, cascading invalidation through dependency trees, privilege-related compilation failures, and pre-deployment validation patterns for Oracle 19c, 21c, and 23ai.
  • ORA-06550 is a compile-time error — the object is marked INVALID and nothing executes until it is fixed
  • The PLS error code after ORA-06550 is the actual diagnosis: PLS-00103 = syntax, PLS-00201 = missing identifier, PLS-00904 = missing privilege
  • CREATE OR REPLACE silently marks objects INVALID on compilation failure — always validate with USER_ERRORS, SHOW ERRORS, or ALTER COMPILE
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
Quick Answer
  • ORA-06550 is a compile-time error — the PL/SQL block has syntax or semantic issues and will not execute until they are fixed
  • It always includes a line number, column number, and a PLS error code that identifies the exact failure category
  • CREATE OR REPLACE does not raise an exception on compilation failure — it silently marks the object INVALID
  • A single invalid object cascades through the dependency tree — all callers are marked INVALID and raise ORA-04063
  • PLS-00904 (insufficient privilege) looks like a code bug but is always a missing GRANT — check privileges before debugging code
🚨 START HERE
ORA-06550 Quick Debug Cheat Sheet
Fast diagnostics for PL/SQL compilation errors. Run these from SQL*Plus, SQLcl, or any Oracle client.
🟡Need to see compilation errors for a specific object
Immediate ActionQuery USER_ERRORS for the object name and type
Commands
SELECT line, position, text FROM user_errors WHERE name = 'BILLING_PKG' AND type = 'PACKAGE BODY' ORDER BY sequence;
SHOW ERRORS PACKAGE BODY BILLING_PKG
Fix NowFix the error at the reported line. Recompile with ALTER PACKAGE BILLING_PKG COMPILE BODY. Query USER_ERRORS again — Oracle reports errors one at a time, so new errors may appear after the first is fixed.
🟡Need to find all INVALID objects in the schema
Immediate ActionQuery USER_OBJECTS for status = INVALID
Commands
SELECT object_name, object_type, last_ddl_time FROM user_objects WHERE status = 'INVALID' ORDER BY object_type, object_name;
SELECT COUNT(*) AS invalid_count, object_type FROM user_objects WHERE status = 'INVALID' GROUP BY object_type ORDER BY COUNT(*) DESC;
Fix NowFind the root cause: query USER_ERRORS for each INVALID object. The one with errors is the root cause. Fix it and recompile. Then run: EXEC UTL_RECOMP.RECOMP_SERIAL; to revalidate all remaining INVALID objects.
🟡Need to see what objects depend on a specific package before deploying a change
Immediate ActionQuery USER_DEPENDENCIES for the package name
Commands
SELECT name, type, referenced_name, referenced_type FROM user_dependencies WHERE referenced_name = 'BILLING_PKG' ORDER BY type, name;
SELECT COUNT(*) AS dependent_count, type FROM user_dependencies WHERE referenced_name = 'BILLING_PKG' GROUP BY type ORDER BY COUNT(*) DESC;
Fix NowThis is your blast radius. Every object in this list will be invalidated if BILLING_PKG fails compilation. Deploy during a low-traffic window and have a rollback plan ready.
🟡Need to check if a missing privilege is causing PLS-00904
Immediate ActionCheck grants on the referenced object for the compiling schema
Commands
SELECT grantor, privilege, grantable FROM dba_tab_privs WHERE grantee = 'BILLING_SCHEMA' AND table_name = 'RESTRICTED_PKG';
SELECT granted_role FROM dba_role_privs WHERE grantee = 'BILLING_SCHEMA';
Fix NowIf no direct grant exists, issue one: GRANT EXECUTE ON other_schema.RESTRICTED_PKG TO BILLING_SCHEMA. For definer's rights packages, role-based grants do not work — the grant must be direct.
Production IncidentCore Billing Package Invalidated After Silent ORA-06550 — 47 Dependent Objects Offline for 2 HoursA developer deployed a package body with a missing semicolon. CREATE OR REPLACE reported success. The package was silently marked INVALID, cascading to 47 dependent objects across three schemas and halting all billing operations.
SymptomAfter a routine deployment at 14:30, all billing-related procedures began failing with ORA-04063: package body 'BILLING_PKG' has errors. The support team saw ORA-04063 — not ORA-06550 — because the callers were reporting the dependency failure, not the compilation failure. The deployment script log showed no errors. Three teams reported failures simultaneously across billing, reporting, and the admin dashboard.
AssumptionThe team assumed the deployment had not been applied correctly and re-ran the script three times. Each run completed without error. Each run produced the same INVALID object. No one checked USER_ERRORS or USER_OBJECTS because the script reported success.
Root causeThe deployment script added a new procedure to BILLING_PKG. The procedure declaration had a missing semicolon after the parameter list: PROCEDURE calculate_discount(p_amount IN NUMBER) IS — the semicolon between the parameter list and IS was missing. CREATE OR REPLACE compiled the package body, found the syntax error, and marked it INVALID. CREATE OR REPLACE does not raise an exception on compilation failure — it completes silently and returns success. The 47 dependent objects were automatically invalidated by Oracle because their dependency on BILLING_PKG could no longer be resolved. The deployment script had no validation step — it did not check USER_ERRORS or USER_OBJECTS after the CREATE OR REPLACE statement.
FixImmediate: queried USER_ERRORS to identify the compilation error. Fixed the missing semicolon. Recompiled the package body. Ran UTL_RECOMP.RECOMP_SERIAL to recompile all remaining INVALID objects. Verified with SELECT COUNT() FROM user_objects WHERE status = 'INVALID'. Structural: added three deployment safeguards. First: SHOW ERRORS after every CREATE OR REPLACE in SQLPlus scripts. Second: a post-deployment validation procedure that queries USER_OBJECTS for INVALID status and USER_ERRORS for compilation errors, raising an exception if any are found. Third: a CI/CD pipeline step that compiles all PL/SQL objects in a staging schema and blocks deployment if any object is INVALID.
Key Lesson
CREATE OR REPLACE never raises an exception on compilation failure — it silently marks the object INVALID and reports success to the calling scriptORA-04063 in production logs means a dependent object is INVALID — the root cause is an ORA-06550 compilation failure in the base object, found by querying USER_ERRORSA single invalid package body cascades to every object in its dependency tree — the blast radius can be dozens of objects across multiple schemasEvery deployment script must validate compilation by checking USER_ERRORS and USER_OBJECTS — trusting CREATE OR REPLACE success is deploying blind
Production Debug GuideFrom compilation error to deployed fix
ORA-06550 with a line and column number when running an anonymous PL/SQL blockRead the line and column numbers — they point to the exact position in the anonymous block. The line number starts at 1 from the first line of the BEGIN...END block (or DECLARE...BEGIN...END). Check for syntax errors at that position: missing semicolons, unmatched parentheses, invalid keywords, undeclared variables.
ORA-06550 followed by PLS-00201: identifier must be declaredThe referenced variable, cursor, type, package, or table does not exist in the current compilation scope. Check spelling first. If the name is correct, verify the object exists: SELECT object_name FROM all_objects WHERE object_name = 'NAME'. If it exists in another schema, check for a missing synonym or a missing GRANT. For definer's rights packages, role-based grants are invisible — issue a direct GRANT.
ORA-06550 followed by PLS-00904: insufficient privilege to access objectThe object exists and the name is correct, but the compiling schema lacks the required privilege. Check: SELECT privilege, grantee, table_name FROM dba_tab_privs WHERE grantee = 'COMPILING_SCHEMA' AND table_name = 'OBJECT_NAME'. If no rows are returned, issue the GRANT. For definer's rights code, role-based grants do not work — the grant must be direct.
ORA-04063 in production — 'package body has errors' — but no deployment was runA dependent object was invalidated by a change to its dependency. Query: SELECT name, type, line, text FROM user_errors ORDER BY name, sequence. The object with errors is the root cause. Fix its compilation error and all dependents will revalidate on next access. If the root cause is in another schema, check DBA_ERRORS.
ORA-06550 after deploying a package body — deployment script reported successCREATE OR REPLACE does not raise on compilation failure. Query USER_ERRORS: SELECT line, position, text FROM user_errors WHERE name = 'PKG_NAME' AND type = 'PACKAGE BODY' ORDER BY sequence. Fix the reported error, recompile, and repeat until no errors remain. Then run UTL_RECOMP.RECOMP_SERIAL to revalidate all dependents.
Multiple ORA-04063 errors across different procedures after a deploymentThis is cascading invalidation from a single root cause. Query USER_OBJECTS for all INVALID objects: SELECT object_name, object_type FROM user_objects WHERE status = 'INVALID' ORDER BY object_type, object_name. Query USER_ERRORS for the one that has compilation errors — that is the root cause. Fix it, recompile it, then use UTL_RECOMP.RECOMP_SERIAL to cascade revalidation.
ORA-06550 error line number does not match the source fileThe line number references the position within the PL/SQL block as received by the compiler, which may differ from your source file line numbering. In SQL*Plus, line 1 is the first line after CREATE OR REPLACE. In anonymous blocks, line 1 is the DECLARE or BEGIN line. If the object was compiled from a concatenated script, the line offset depends on where the CREATE OR REPLACE starts in the script.

ORA-06550: line %s, column %s is a PL/SQL compilation error. It occurs when the Oracle compiler encounters a syntax error, a semantic error, or a privilege issue while compiling a PL/SQL block, package, procedure, function, or trigger. The error includes line and column numbers that point to the exact location of the failure, followed by a PLS error code that identifies the specific failure category.

Unlike runtime errors that occur during execution, ORA-06550 prevents execution entirely. The compiled object is marked INVALID in the data dictionary. Any object that depends on the invalid object — callers, triggers, views referencing the function — is also marked INVALID and will raise ORA-04063 or ORA-06508 when invoked.

The cascading effect is the production danger. A single ORA-06550 in a core package body can invalidate dozens of dependent procedures, triggers, and views across multiple schemas. The error appears at deployment time, but its impact propagates through the entire dependency tree. Engineers who see ORA-04063 in production logs do not immediately connect it to the ORA-06550 that caused the cascade — they see the dependent failure, not the root cause.

This guide covers the full lifecycle of ORA-06550: reading PLS error codes, distinguishing syntax from semantic errors, understanding cascading invalidation, diagnosing privilege failures, safe deployment patterns, batch recompilation with UTL_RECOMP, and CI/CD validation. All examples are runnable on Oracle 19c, 21c, and 23ai.

ORA-06550 Anatomy: Line, Column, and PLS Error Code

ORA-06550 always includes three components: the line number, the column number, and a PLS error code that identifies the specific failure category. The line and column reference the position within the PL/SQL block being compiled — not the position in your source file or deployment script.

The PLS error code that follows ORA-06550 is the diagnostic that matters. ORA-06550 tells you where. The PLS code tells you what. Common PLS codes:

PLS-00103 (Encountered symbol X when expecting Y) is the most common syntax error. The compiler found an unexpected token. The error message shows both what was found and what was expected, which usually makes the fix obvious.

PLS-00201 (identifier X must be declared) means a referenced identifier — variable, cursor, type, table, package — does not exist in the current compilation scope. Causes: typo in the name, missing declaration, missing synonym, missing GRANT, or referencing an object that has not been created yet.

PLS-00302 (component X must be declared) means a field or method does not exist on a referenced type, record, or package. Causes: the field was renamed, removed, or never existed in the type definition.

PLS-00306 (wrong number or types of arguments) means the call to a procedure or function has the wrong parameter count or incompatible parameter types. Causes: a parameter was added or removed from the called procedure, or a data type mismatch.

PLS-00904 (insufficient privilege to access object) means the compiling schema lacks EXECUTE or SELECT privilege on a referenced object. The object exists and the name is correct — the privilege is missing. This is the error that wastes the most debugging time because engineers look at the code instead of the grants.

Oracle reports one error per compilation attempt. Fix the first error, recompile, and the next error appears. Do not assume a single fix resolves all errors — repeat until USER_ERRORS returns zero rows for the object.

ora06550_examples.sql · SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
-- ============================================================
-- Setup: create a table and helper package for the examples
-- Runnable on Oracle 19c, 21c, 23ai
-- ============================================================

CREATE TABLE demo_accounts (
  account_id   NUMBER PRIMARY KEY,
  account_type VARCHAR2(30),
  balance      NUMBER(15,2)
);

INSERT INTO demo_accounts VALUES (1, 'CHECKING', 5000);
COMMIT;

CREATE OR REPLACE PACKAGE demo_util_pkg AS
  FUNCTION calculate_tax(p_amount IN NUMBER, p_region IN VARCHAR2) RETURN NUMBER;
END demo_util_pkg;
/

CREATE OR REPLACE PACKAGE BODY demo_util_pkg AS
  FUNCTION calculate_tax(p_amount IN NUMBER, p_region IN VARCHAR2) RETURN NUMBER IS
  BEGIN
    RETURN p_amount * 0.08;
  END calculate_tax;
END demo_util_pkg;
/

-- ============================================================
-- Example 1: PLS-00103 — Missing semicolon after variable declaration
-- ============================================================

CREATE OR REPLACE PROCEDURE demo_syntax_error IS
  v_count NUMBER   -- Missing semicolon here
BEGIN
  SELECT COUNT(*) INTO v_count FROM demo_accounts;
END demo_syntax_error;
/

-- Error output:
-- ORA-06550: line 3, column 1:
-- PLS-00103: Encountered the symbol "BEGIN" when expecting one of the following:
--    := . ( @ % ; not null range default character

-- Fix: add semicolon after NUMBER on line 2

SHOW ERRORS PROCEDURE DEMO_SYNTAX_ERROR

-- ============================================================
-- Example 2: PLS-00201 — Undeclared variable
-- ============================================================

CREATE OR REPLACE PROCEDURE demo_undeclared_var IS
BEGIN
  v_total := 100;   -- v_total was never declared
  DBMS_OUTPUT.PUT_LINE(v_total);
END demo_undeclared_var;
/

-- Error output:
-- ORA-06550: line 3, column 3:
-- PLS-00201: identifier 'V_TOTAL' must be declared

-- Fix: add DECLARE section with v_total NUMBER;

-- ============================================================
-- Example 3: PLS-00306 — Wrong number of arguments
-- ============================================================

CREATE OR REPLACE PROCEDURE demo_wrong_args IS
  v_result NUMBER;
BEGIN
  -- calculate_tax expects 2 args (amount, region) — we passed 3
  v_result := demo_util_pkg.calculate_tax(100, 'CA', 'EXTRA');
END demo_wrong_args;
/

-- Error output:
-- ORA-06550: line 5, column 16:
-- PLS-00306: wrong number or types of arguments in call to 'CALCULATE_TAX'

-- Fix: remove the extra argument

-- ============================================================
-- Example 4: PLS-00302 — Component not declared on record type
-- ============================================================

CREATE OR REPLACE PROCEDURE demo_missing_component IS
  v_rec demo_accounts%ROWTYPE;
BEGIN
  SELECT * INTO v_rec FROM demo_accounts WHERE account_id = 1;
  DBMS_OUTPUT.PUT_LINE(v_rec.nonexistent_field);  -- Field does not exist
END demo_missing_component;
/

-- Error output:
-- ORA-06550: line 5, column 32:
-- PLS-00302: component 'NONEXISTENT_FIELD' must be declared

-- Fix: use a valid column name from demo_accounts (e.g., v_rec.balance)

-- ============================================================
-- Retrieving all compilation errors for a specific object
-- ============================================================

SELECT
  name,
  type,
  line,
  position,
  text AS error_text
FROM user_errors
WHERE name = 'DEMO_SYNTAX_ERROR'
ORDER BY sequence;

-- Cleanup
-- DROP PROCEDURE demo_syntax_error;
-- DROP PROCEDURE demo_undeclared_var;
-- DROP PROCEDURE demo_wrong_args;
-- DROP PROCEDURE demo_missing_component;
-- DROP PACKAGE demo_util_pkg;
-- DROP TABLE demo_accounts PURGE;
Mental Model
PLS Error Code Quick Reference
The PLS code after ORA-06550 is the actual diagnosis. ORA-06550 tells you where. The PLS code tells you what.
  • PLS-00103: Syntax error — the compiler found an unexpected token. The message shows what it found and what it expected. Fix the grammar.
  • PLS-00201: Identifier not declared — a variable, table, package, or type does not exist in scope. Check spelling, then check the database for the object.
  • PLS-00302: Component not declared — a field or method does not exist on the referenced type or package. Check the type definition.
  • PLS-00306: Wrong arguments — the parameter count or types do not match the called procedure. Check the procedure specification.
  • PLS-00904: Insufficient privilege — the object exists but the compiling schema lacks EXECUTE or SELECT privilege. Fix with a GRANT, not a code change.
  • Oracle reports errors one at a time per compilation — fix the first, recompile, check for the next.
📊 Production Insight
Engineers who read only ORA-06550 are debugging with half the information. The PLS code that follows it narrows the diagnosis to a specific category — syntax, declaration, privilege, or argument mismatch — and determines whether the fix is in the code, the database objects, or the grants.
🎯 Key Takeaway
ORA-06550 gives the location (line and column). The PLS code gives the category (syntax, semantic, privilege). Both are required for diagnosis. Oracle reports one error per compilation — fix, recompile, and repeat until USER_ERRORS returns zero rows.

Syntax Errors vs Semantic Errors

ORA-06550 errors fall into two categories that require fundamentally different fix strategies.

Syntax errors mean the code does not follow PL/SQL grammar rules. The compiler cannot parse the text into a valid program structure. Missing semicolons, unmatched parentheses, invalid keywords, and malformed expressions are all syntax errors. PLS-00103 is always a syntax error. The fix is always a code change — correct the grammar at the reported line and column.

Semantic errors mean the code is grammatically correct but references something that does not exist or is used incorrectly in context. The compiler can parse the code but cannot resolve identifiers, types, or privileges. PLS-00201, PLS-00302, PLS-00306, and PLS-00904 are all semantic errors.

The critical difference for debugging: syntax errors are always in the code. Semantic errors may be in the code (typo, wrong argument count) or outside the code (missing GRANT, dropped table, renamed package). When you see a semantic error on code that looks correct, stop debugging the code and start investigating the database state — the object, the synonym, the grant.

syntax_vs_semantic.sql · SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
-- ============================================================
-- SYNTAX ERRORS — the code violates PL/SQL grammar
-- Fix: correct the code structure
-- ============================================================

-- Missing semicolon — PLS-00103
CREATE OR REPLACE PROCEDURE demo_missing_semicolon IS
  v_count NUMBER   -- Semicolon missing here
BEGIN
  NULL;
END demo_missing_semicolon;
/
-- Fix: add ; after NUMBER

-- Unmatched parenthesis — PLS-00103
CREATE OR REPLACE PROCEDURE demo_unmatched_paren IS
  v_result NUMBER;
BEGIN
  v_result := demo_util_pkg.calculate_tax(100, 'CA';  -- Missing closing )
END demo_unmatched_paren;
/
-- Fix: add closing ) before ;

-- ============================================================
-- SEMANTIC ERRORS — code is grammatically correct but references
-- something that does not exist or cannot be accessed
-- Fix: may be code, database objects, or grants
-- ============================================================

-- Undeclared variable — PLS-00201 — fix is in the code
CREATE OR REPLACE PROCEDURE demo_undeclared IS
BEGIN
  v_total := 100;  -- v_total not declared
END demo_undeclared;
/
-- Fix: add v_total NUMBER; in the DECLARE section

-- Missing table — PLS-00201 — fix is a database object or synonym
CREATE OR REPLACE PROCEDURE demo_missing_table IS
  v_name VARCHAR2(100);
BEGIN
  SELECT name INTO v_name FROM nonexistent_table_xyz WHERE id = 1;
END demo_missing_table;
/
-- Fix: create the table, create a synonym to the correct table,
--   or correct the table name

-- Missing privilege — PLS-00904 — fix is a GRANT
CREATE OR REPLACE PROCEDURE demo_privilege_error IS
BEGIN
  other_schema.restricted_pkg.do_something;
END demo_privilege_error;
/
-- Fix: GRANT EXECUTE ON other_schema.restricted_pkg TO current_schema;
-- The code is correct — the privilege is missing

-- ============================================================
-- Diagnostic: determine if the referenced object exists
-- Run this when PLS-00201 or PLS-00904 appears
-- ============================================================

-- Check if the object exists anywhere in the database
SELECT owner, object_name, object_type, status
FROM all_objects
WHERE object_name = 'RESTRICTED_PKG'
ORDER BY owner;

-- Check if a synonym exists for the object
SELECT owner, synonym_name, table_owner, table_name
FROM all_synonyms
WHERE synonym_name = 'RESTRICTED_PKG';

-- Check direct grants on the object
SELECT grantor, grantee, privilege, grantable
FROM dba_tab_privs
WHERE table_name = 'RESTRICTED_PKG'
  AND grantee = 'CURRENT_SCHEMA'
ORDER BY privilege;
💡Syntax vs Semantic Error Resolution Strategy
When you see PLS-00103: fix the code at the reported line. The compiler tells you what it found and what it expected. When you see PLS-00201, PLS-00302, or PLS-00904: before touching the code, run the diagnostic queries above. The object may exist in another schema without a synonym, the grant may be missing, or the name may have changed. The most common waste of time on ORA-06550 is debugging code that is correct when the problem is a missing grant.
📊 Production Insight
PLS-00904 is the compilation error that wastes the most debugging time in multi-schema environments. The code looks correct. The object exists. The name is spelled right. Engineers spend hours staring at the procedure body when the fix is a single GRANT EXECUTE statement. Check dba_tab_privs before opening the source file.
🎯 Key Takeaway
Syntax errors are always in the code — fix the grammar. Semantic errors may be in the code, the database objects, or the grants. When PLS-00201 or PLS-00904 appears on code that looks correct, stop debugging the code and start checking the database: object existence, synonyms, grants.

Cascading Invalidation: ORA-06550 to ORA-04063

When a PL/SQL object fails compilation, Oracle marks it INVALID in USER_OBJECTS. Every object that depends on the invalid object is also marked INVALID automatically. This cascading invalidation is the mechanism that turns a single compilation error into a multi-team production outage.

The dependency chain flows downward through every reference type. If a package body is invalid, all procedures that call any procedure in that package become invalid. If a function is invalid, all views that reference the function become invalid. If a type is invalid, all tables that use the type and all procedures that reference the type become invalid.

The error that callers see is ORA-04063: package body 'X' has errors — not ORA-06550. This is critical for incident response. When ORA-04063 appears in production logs, the root cause is not in the failing procedure — it is in the invalid dependency. Query USER_ERRORS for the object named in the ORA-04063 message to find the original compilation error.

Oracle automatically revalidates dependent objects on next access once the base object compiles successfully. You do not need to manually recompile every dependent. However, revalidation happens lazily — each dependent is recompiled when it is first called, adding a brief latency to the first call. For production systems where this latency matters, use UTL_RECOMP.RECOMP_SERIAL or DBMS_UTILITY.COMPILE_SCHEMA to force immediate recompilation of all invalid objects.

cascading_invalidation.sql · SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
-- ============================================================
-- Demonstrate cascading invalidation
-- ============================================================

-- Base package — the root of the dependency tree
CREATE OR REPLACE PACKAGE base_pkg AS
  FUNCTION get_tax_rate(p_region IN VARCHAR2) RETURN NUMBER;
END base_pkg;
/

CREATE OR REPLACE PACKAGE BODY base_pkg AS
  FUNCTION get_tax_rate(p_region IN VARCHAR2) RETURN NUMBER IS
  BEGIN
    RETURN 0.08;
  END get_tax_rate;
END base_pkg;
/

-- Dependent procedure — calls base_pkg
CREATE OR REPLACE PROCEDURE calc_order_total(
  p_amount IN NUMBER,
  p_region IN VARCHAR2,
  p_total  OUT NUMBER
) IS
  v_tax_rate NUMBER;
BEGIN
  v_tax_rate := base_pkg.get_tax_rate(p_region);
  p_total := p_amount + (p_amount * v_tax_rate);
END calc_order_total;
/

-- Dependent view — references a function from base_pkg
CREATE OR REPLACE VIEW order_tax_view AS
SELECT
  a.account_id,
  a.balance,
  base_pkg.get_tax_rate('US') AS tax_rate,
  a.balance * (1 + base_pkg.get_tax_rate('US')) AS total_with_tax
FROM demo_accounts a;

-- Verify: all objects are VALID
SELECT object_name, object_type, status
FROM user_objects
WHERE object_name IN ('BASE_PKG', 'CALC_ORDER_TOTAL', 'ORDER_TAX_VIEW')
ORDER BY object_type;
-- Expected: all status = VALID

-- ============================================================
-- Now break the base package — introduce a compilation error
-- ============================================================

CREATE OR REPLACE PACKAGE BODY base_pkg AS
  FUNCTION get_tax_rate(p_region IN VARCHAR2) RETURN NUMBER IS
  BEGIN
    RETURN 0.08   -- Missing semicolon — ORA-06550
  END get_tax_rate;
END base_pkg;
/

-- Check: base_pkg is now INVALID
-- Check: dependents are also INVALID
SELECT object_name, object_type, status
FROM user_objects
WHERE object_name IN ('BASE_PKG', 'CALC_ORDER_TOTAL', 'ORDER_TAX_VIEW')
ORDER BY object_type;
-- Expected: all status = INVALID

-- Try to call the dependent procedure — ORA-04063
DECLARE
  v_total NUMBER;
BEGIN
  calc_order_total(100, 'US', v_total);
EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
    -- ORA-04063: package body "DEMO.BASE_PKG" has errors
END;
/

-- ============================================================
-- Fix: correct the base package, recompile, revalidate
-- ============================================================

-- Step 1: Find the root cause
SELECT name, type, line, position, text
FROM user_errors
WHERE name = 'BASE_PKG'
ORDER BY sequence;

-- Step 2: Fix and recompile the base package
CREATE OR REPLACE PACKAGE BODY base_pkg AS
  FUNCTION get_tax_rate(p_region IN VARCHAR2) RETURN NUMBER IS
  BEGIN
    RETURN 0.08;  -- Fixed: semicolon added
  END get_tax_rate;
END base_pkg;
/

-- Step 3: Verify base package is VALID
SELECT status FROM user_objects
WHERE object_name = 'BASE_PKG' AND object_type = 'PACKAGE BODY';

-- Step 4: Force revalidation of all INVALID objects
-- Option A: UTL_RECOMP (recommended for production)
EXEC UTL_RECOMP.RECOMP_SERIAL;

-- Option B: DBMS_UTILITY.COMPILE_SCHEMA (recompiles everything)
EXEC DBMS_UTILITY.COMPILE_SCHEMA(schema => USER, compile_all => FALSE);
-- compile_all => FALSE: only recompiles INVALID objects

-- Step 5: Verify all objects are VALID
SELECT object_name, object_type, status
FROM user_objects
WHERE object_name IN ('BASE_PKG', 'CALC_ORDER_TOTAL', 'ORDER_TAX_VIEW')
ORDER BY object_type;
-- Expected: all status = VALID

-- ============================================================
-- View the dependency tree before deploying changes
-- Run this BEFORE deploying to estimate the blast radius
-- ============================================================

SELECT
  name         AS dependent_name,
  type         AS dependent_type,
  referenced_name,
  referenced_type
FROM user_dependencies
WHERE referenced_name = 'BASE_PKG'
ORDER BY type, name;

-- Count dependents by type
SELECT type, COUNT(*) AS dependent_count
FROM user_dependencies
WHERE referenced_name = 'BASE_PKG'
GROUP BY type
ORDER BY COUNT(*) DESC;

-- Cleanup
-- DROP VIEW order_tax_view;
-- DROP PROCEDURE calc_order_total;
-- DROP PACKAGE base_pkg;
⚠ Cascading Invalidation Blast Radius
📊 Production Insight
The incident in this article had 47 dependents. The on-call team saw 47 separate ORA-04063 errors across three schemas and initially treated it as 47 separate problems. It was one problem — a single compilation error in a base package. Always check USER_DEPENDENCIES for the root cause before investigating individual failures.
🎯 Key Takeaway
A single ORA-06550 in a core package cascades to every object in its dependency tree. Callers see ORA-04063, not ORA-06550 — trace back to the root cause via USER_ERRORS. Fix the base object first — dependents revalidate automatically. Use UTL_RECOMP.RECOMP_SERIAL to force immediate revalidation in production.

CREATE OR REPLACE: The Silent Deployment Failure

CREATE OR REPLACE is the most dangerous deployment command in Oracle because it completes successfully even when compilation fails. The object is silently marked INVALID and the deployment script reports success. No exception is raised. No error message appears in the script output. The deployment log looks clean.

This behavior exists by design — Oracle allows creating invalid objects to support forward references and circular dependencies during development. Two packages that reference each other cannot both be valid during creation — one must be created first in an invalid state. CREATE OR REPLACE accommodates this by not raising on compilation failure.

But in deployment scripts, this behavior is dangerous. A typo, a missing grant, or a renamed column causes compilation failure. The deployment script finishes without error. The team marks the deployment as successful. Hours later, ORA-04063 errors begin appearing across the application as callers attempt to invoke the invalid object.

The fix is to never trust CREATE OR REPLACE success as an indicator of valid compilation. Always validate by checking USER_ERRORS after every CREATE OR REPLACE, or by using ALTER COMPILE which does raise on failure. SQLPlus SHOW ERRORS displays the last compilation errors — add it after every CREATE OR REPLACE in SQLPlus scripts.

safe_deployment.sql · SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
-- ============================================================
-- UNSAFE: CREATE OR REPLACE with no validation
-- The script reports success even if the object is INVALID
-- ============================================================

CREATE OR REPLACE PROCEDURE unsafe_deploy_example IS
  v_count NUMBER   -- Missing semicolon
BEGIN
  NULL;
END unsafe_deploy_example;
/
-- SQL*Plus output: "Procedure created."
-- But the procedure is INVALID — no error is raised

-- ============================================================
-- SAFE Pattern 1: SHOW ERRORS after every CREATE OR REPLACE
-- For SQL*Plus and SQLcl deployment scripts
-- ============================================================

CREATE OR REPLACE PROCEDURE safe_deploy_example IS
  v_count NUMBER;
BEGIN
  SELECT COUNT(*) INTO v_count FROM demo_accounts;
END safe_deploy_example;
/

SHOW ERRORS PROCEDURE SAFE_DEPLOY_EXAMPLE
-- If compilation errors exist, they are displayed here
-- Add WHENEVER SQLERROR EXIT to abort the script on errors

-- ============================================================
-- SAFE Pattern 2: Query USER_ERRORS after deployment
-- For programmatic deployment tools (Flyway, Liquibase, custom scripts)
-- ============================================================

DECLARE
  v_error_count NUMBER;
  v_error_text  VARCHAR2(4000);
BEGIN
  SELECT COUNT(*) INTO v_error_count
  FROM user_errors
  WHERE name = 'SAFE_DEPLOY_EXAMPLE'
    AND type = 'PROCEDURE';

  IF v_error_count > 0 THEN
    -- Get the first error for the exception message
    SELECT text INTO v_error_text
    FROM user_errors
    WHERE name = 'SAFE_DEPLOY_EXAMPLE'
      AND type = 'PROCEDURE'
    ORDER BY sequence
    FETCH FIRST 1 ROW ONLY;

    RAISE_APPLICATION_ERROR(
      -20060,
      'Deployment failed: SAFE_DEPLOY_EXAMPLE has ' || v_error_count
      || ' compilation error(s). First error: ' || v_error_text
    );
  END IF;

  DBMS_OUTPUT.PUT_LINE('SAFE_DEPLOY_EXAMPLE compiled successfully');
END;
/

-- ============================================================
-- SAFE Pattern 3: ALTER COMPILE — raises on failure
-- Use for validation after CREATE OR REPLACE
-- ============================================================

-- ALTER COMPILE raises an exception if compilation fails
-- Unlike CREATE OR REPLACE, it does NOT complete silently
ALTER PROCEDURE safe_deploy_example COMPILE;
-- If successful: no output, object is VALID
-- If failed: ORA-24344 or ORA-06550 is raised

-- For packages:
ALTER PACKAGE billing_pkg COMPILE;        -- Compiles the spec
ALTER PACKAGE billing_pkg COMPILE BODY;   -- Compiles the body

-- ============================================================
-- SAFE Pattern 4: Post-deployment health check
-- Run after the entire deployment to catch any INVALID objects
-- ============================================================

CREATE OR REPLACE PROCEDURE post_deployment_check IS
  v_invalid_count NUMBER;
BEGIN
  SELECT COUNT(*) INTO v_invalid_count
  FROM user_objects
  WHERE status = 'INVALID';

  IF v_invalid_count > 0 THEN
    FOR rec IN (
      SELECT o.object_name, o.object_type,
             NVL((SELECT e.text FROM user_errors e WHERE e.name = o.object_name
                  ORDER BY e.sequence FETCH FIRST 1 ROW ONLY), 'no error details') AS first_error
      FROM user_objects o
      WHERE o.status = 'INVALID'
      ORDER BY o.object_type, o.object_name
    ) LOOP
      DBMS_OUTPUT.PUT_LINE(
        'INVALID: ' || rec.object_type || ' ' || rec.object_name
        || ' — ' || rec.first_error
      );
    END LOOP;

    RAISE_APPLICATION_ERROR(
      -20061,
      'Post-deployment check failed: ' || v_invalid_count
      || ' object(s) are INVALID. Check DBMS_OUTPUT for details.'
    );
  END IF;

  DBMS_OUTPUT.PUT_LINE('Post-deployment check passed: all objects VALID');
END post_deployment_check;
/

-- Run after every deployment:
EXEC post_deployment_check;
⚠ CREATE OR REPLACE Is Not a Validation Tool
📊 Production Insight
The production incident in this article happened because the deployment script used CREATE OR REPLACE without validation. The script ran three times, producing the same INVALID object each time, and reported success each time. Adding SHOW ERRORS or a USER_ERRORS check after each CREATE OR REPLACE would have caught the error on the first run — before it cascaded to 47 dependents.
🎯 Key Takeaway
CREATE OR REPLACE silently marks objects INVALID on compilation failure. No exception, no error output, no indication of failure. Always validate with USER_ERRORS, SHOW ERRORS, or ALTER COMPILE. If your deployment script does not check compilation status, you are deploying blind.

PLS-00904: insufficient privilege to access object is the compilation error that costs the most debugging time. The code is correct. The referenced object exists. The name is spelled correctly. The compilation still fails because the compiling schema lacks the required privilege on the referenced object.

This error appears most frequently in multi-schema environments where packages in one schema reference tables or packages in another schema. The compiling schema needs EXECUTE privilege on referenced packages and SELECT privilege on referenced tables. Without these grants, compilation fails.

The critical subtlety is the distinction between definer's rights and invoker's rights. The default for PL/SQL packages is definer's rights — the package runs with the owner's privileges, and compilation checks the owner's direct grants. Role-based grants are invisible during compilation of definer's rights code. If the EXECUTE privilege is granted through a role but not directly, PLS-00904 fires.

Invoker's rights packages (created with AUTHID CURRENT_USER) can use role-based grants because they run with the calling user's privileges at execution time. The compilation still checks direct grants, but execution resolves references using the invoker's privilege set.

The fix for PLS-00904 is always a GRANT — never a code change. Issue the grant directly to the schema owner, not through a role, unless the package uses AUTHID CURRENT_USER.

privilege_compilation.sql · SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
-- ============================================================
-- PLS-00904 scenarios and fixes
-- ============================================================

-- Scenario 1: Missing EXECUTE grant on another schema's package
-- This procedure references FINANCE_SCHEMA.FINANCE_PKG

CREATE OR REPLACE PROCEDURE call_finance_validation IS
BEGIN
  finance_schema.finance_pkg.validate_account(12345);
  -- PLS-00904: insufficient privilege to access object FINANCE_PKG
END call_finance_validation;
/

-- Fix: direct grant to the compiling schema
-- Run as FINANCE_SCHEMA or a DBA:
GRANT EXECUTE ON finance_schema.finance_pkg TO billing_schema;

-- Then recompile:
ALTER PROCEDURE call_finance_validation COMPILE;

-- ============================================================
-- Scenario 2: Role-based grant does NOT work for definer's rights
-- ============================================================

-- This grant goes through a role:
GRANT EXECUTE ON finance_schema.finance_pkg TO app_role;
GRANT app_role TO billing_schema;
-- billing_schema has the privilege via the role
-- But definer's rights compilation STILL raises PLS-00904

-- The fix: use a direct grant, or switch to invoker's rights

-- ============================================================
-- Scenario 3: Invoker's rights — role-based grants work
-- ============================================================

CREATE OR REPLACE PROCEDURE call_finance_validation_ir
  AUTHID CURRENT_USER   -- Invoker's rights
IS
BEGIN
  finance_schema.finance_pkg.validate_account(12345);
  -- Resolves at execution time using the caller's privileges
  -- Role-based grants work because they are active during execution
END call_finance_validation_ir;
/

-- ============================================================
-- Diagnostic: find all PLS-00904 errors in the schema
-- ============================================================

SELECT
  name,
  type,
  line,
  text
FROM user_errors
WHERE text LIKE '%PLS-00904%'
ORDER BY name, sequence;

-- ============================================================
-- Diagnostic: check what direct grants exist for the compiling schema
-- ============================================================

SELECT
  grantor,
  privilege,
  table_name AS object_name,
  grantable
FROM dba_tab_privs
WHERE grantee = 'BILLING_SCHEMA'
  AND privilege = 'EXECUTE'
ORDER BY table_name;

-- Check role-based grants (will not help for definer's rights)
SELECT
  granted_role,
  admin_option
FROM dba_role_privs
WHERE grantee = 'BILLING_SCHEMA'
ORDER BY granted_role;
Mental Model
PL/SQL Privilege Model for Compilation
Definer's rights sees direct grants only. Invoker's rights sees everything the caller has.
  • Definer's rights (default): compiles using the owner's direct grants only. Role-based grants are invisible. PLS-00904 fires if the direct grant is missing.
  • Invoker's rights (AUTHID CURRENT_USER): compiles checking direct grants, but executes using the caller's full privilege set including roles.
  • PLS-00904 during compilation means the compiling schema lacks a direct EXECUTE or SELECT grant on a referenced object.
  • The fix is always a GRANT, never a code change. Check dba_tab_privs before debugging the procedure body.
  • If converting to invoker's rights, remember that the procedure now runs with different privileges for each caller — test accordingly.
📊 Production Insight
In multi-schema environments, PLS-00904 is the most common compilation error after a new dependency is added. The developer codes the cross-schema reference, tests it under a DBA account where all privileges exist, and pushes to production where the application schema lacks the grant. Add GRANT statements to deployment scripts for every new cross-schema reference.
🎯 Key Takeaway
PLS-00904 is a privilege error that looks like a code error. The code is correct — the GRANT is missing. Definer's rights requires direct grants — role-based grants are invisible during compilation. Check dba_tab_privs before debugging the code.

Pre-Deployment Compilation Validation and CI/CD

Every deployment pipeline should treat PL/SQL compilation as a build step that can fail. The pattern is the same as any compiled language: compile, check for errors, abort if any are found.

For SQL*Plus deployment scripts, add WHENEVER SQLERROR EXIT FAILURE at the top and SHOW ERRORS after every CREATE OR REPLACE. This combination causes the script to abort on the first error instead of continuing blind.

For CI/CD pipelines (Jenkins, GitHub Actions, GitLab CI), add a post-deployment step that runs a validation procedure: query USER_OBJECTS for INVALID status, query USER_ERRORS for compilation errors, and fail the pipeline if any are found. The pipeline should not mark the deployment as successful until all objects are VALID.

For staging environments, compile all PL/SQL objects in a clean schema that mirrors production. If any object fails compilation, block the deployment. This catches ORA-06550 before it reaches production — including privilege errors that only appear in the production grant model.

UTL_RECOMP.RECOMP_SERIAL is the recommended utility for batch recompilation in production. It recompiles all INVALID objects in dependency order, ensuring that base objects are compiled before their dependents. DBMS_UTILITY.COMPILE_SCHEMA is an alternative that accepts a compile_all parameter — set it to FALSE to recompile only INVALID objects.

cicd_validation.sql · SQL
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
-- ============================================================
-- SQL*Plus deployment script with compilation validation
-- ============================================================

-- Abort script on any error
WHENEVER SQLERROR EXIT FAILURE ROLLBACK

SET SERVEROUTPUT ON

PROMPT Deploying BILLING_PKG specification...

CREATE OR REPLACE PACKAGE billing_pkg AS
  PROCEDURE process_payment(p_account_id IN NUMBER, p_amount IN NUMBER);
  FUNCTION get_balance(p_account_id IN NUMBER) RETURN NUMBER;
END billing_pkg;
/

SHOW ERRORS PACKAGE BILLING_PKG

PROMPT Deploying BILLING_PKG body...

CREATE OR REPLACE PACKAGE BODY billing_pkg AS
  PROCEDURE process_payment(p_account_id IN NUMBER, p_amount IN NUMBER) IS
    v_balance NUMBER;
  BEGIN
    v_balance := get_balance(p_account_id);
    IF v_balance < p_amount THEN
      RAISE_APPLICATION_ERROR(-20001, 'Insufficient balance');
    END IF;
    UPDATE demo_accounts SET balance = balance - p_amount
    WHERE account_id = p_account_id;
  END process_payment;

  FUNCTION get_balance(p_account_id IN NUMBER) RETURN NUMBER IS
    v_balance NUMBER;
  BEGIN
    SELECT balance INTO v_balance FROM demo_accounts
    WHERE account_id = p_account_id;
    RETURN v_balance;
  EXCEPTION
    WHEN NO_DATA_FOUND THEN
      RAISE_APPLICATION_ERROR(-20002, 'Account not found: ' || p_account_id);
  END get_balance;
END billing_pkg;
/

SHOW ERRORS PACKAGE BODY BILLING_PKG

-- ============================================================
-- Post-deployment validation — run after all objects are deployed
-- ============================================================

PROMPT Running post-deployment validation...

DECLARE
  v_invalid_count NUMBER;
BEGIN
  -- Recompile any remaining INVALID objects
  UTL_RECOMP.RECOMP_SERIAL;

  -- Check for any remaining INVALID objects
  SELECT COUNT(*) INTO v_invalid_count
  FROM user_objects
  WHERE status = 'INVALID';

  IF v_invalid_count > 0 THEN
    DBMS_OUTPUT.PUT_LINE('WARNING: ' || v_invalid_count || ' INVALID objects remain:');
    FOR rec IN (
      SELECT object_name, object_type FROM user_objects
      WHERE status = 'INVALID'
      ORDER BY object_type, object_name
    ) LOOP
      DBMS_OUTPUT.PUT_LINE('  ' || rec.object_type || ' ' || rec.object_name);
    END LOOP;

    RAISE_APPLICATION_ERROR(
      -20062,
      'Post-deployment validation failed: ' || v_invalid_count || ' INVALID objects'
    );
  END IF;

  DBMS_OUTPUT.PUT_LINE('Post-deployment validation passed: all objects VALID');
END;
/

PROMPT Deployment complete.

-- ============================================================
-- CI/CD pipeline compilation check (conceptual — adapt to your pipeline)
-- Run in a staging schema before deploying to production
-- ============================================================

-- Step 1: Deploy all objects to the staging schema
-- (handled by your CI/CD tool — Flyway, Liquibase, custom scripts)

-- Step 2: Compile all objects and check for errors
BEGIN
  UTL_RECOMP.RECOMP_SERIAL;
END;
/

-- Step 3: Fail the pipeline if any objects are INVALID
DECLARE
  v_invalid_count NUMBER;
BEGIN
  SELECT COUNT(*) INTO v_invalid_count
  FROM user_objects
  WHERE status = 'INVALID'
    AND object_type IN ('PACKAGE', 'PACKAGE BODY', 'PROCEDURE', 'FUNCTION', 'TRIGGER');

  IF v_invalid_count > 0 THEN
    -- Output errors for the CI log
    FOR rec IN (
      SELECT o.object_name, o.object_type, e.line, e.text AS error_text
      FROM user_objects o
      LEFT JOIN user_errors e ON e.name = o.object_name AND e.type = o.object_type
      WHERE o.status = 'INVALID'
      ORDER BY o.object_name, e.sequence
    ) LOOP
      DBMS_OUTPUT.PUT_LINE(
        rec.object_type || ' ' || rec.object_name
        || CASE WHEN rec.line IS NOT NULL
           THEN ' Line ' || rec.line || ': ' || rec.error_text
           ELSE ' (no error details — dependency invalidation)'
           END
      );
    END LOOP;

    RAISE_APPLICATION_ERROR(-20063, 'CI compilation check failed');
  END IF;
END;
/
💡Deployment Compilation Checklist
  • SQL*Plus scripts: add WHENEVER SQLERROR EXIT FAILURE at the top and SHOW ERRORS after every CREATE OR REPLACE
  • Programmatic tools: query USER_ERRORS after every CREATE OR REPLACE and abort if any errors exist
  • Post-deployment: run UTL_RECOMP.RECOMP_SERIAL to revalidate any cascading invalidations, then check USER_OBJECTS for remaining INVALID objects
  • CI/CD pipeline: compile all objects in a staging schema before deploying to production — fail the pipeline on any INVALID object
  • Never mark a deployment as successful without confirming zero INVALID objects in the target schema
📊 Production Insight
The gap between CREATE OR REPLACE completing and the first ORA-04063 appearing in production is the window where silent deployment failures live. In the production incident, that gap was 45 minutes — the time between the deployment at 14:30 and the first billing operation at 15:15. A post-deployment validation step running immediately after the script would have caught the error in seconds.
🎯 Key Takeaway
Treat PL/SQL compilation as a build step that can fail. Validate with USER_ERRORS after every CREATE OR REPLACE. Use UTL_RECOMP.RECOMP_SERIAL for batch recompilation. Fail the CI/CD pipeline if any object is INVALID. The cost of validation is seconds. The cost of a silent INVALID deployment is hours.

ORA-06550 Through Application Drivers

Application developers encounter ORA-06550 in two distinct contexts. The first is when executing an anonymous PL/SQL block that contains a compilation error. The second — more common in production — is when calling a stored procedure that depends on an INVALID object, producing ORA-04063 or ORA-06508.

ORA-06550 from an anonymous block appears in the application driver as a standard database error with the full error text preserved. The line and column numbers reference the anonymous block text, not a stored object.

ORA-04063 and ORA-06508 appear when the application calls a stored procedure whose dependency tree contains an INVALID object. The error message includes the invalid object name. The application team sees the call fail but cannot see the underlying compilation error — only the DBA or deployment team can query USER_ERRORS for the root cause.

In a well-designed application stack, ORA-04063 should trigger an immediate alert to the database or deployment team — it indicates a deployment issue, not an application bug. The application layer should not retry ORA-04063 — it will not resolve on its own. Log the error with full context and escalate.

driver_compilation_errors.txt · TEXT
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
============================================================
JAVA / JDBCORA-06550 and ORA-04063 handling
============================================================

try {
    CallableStatement cs = conn.prepareCall(
        "BEGIN billing_pkg.process_payment(?, ?); END;"
    );
    cs.setInt(1, accountId);
    cs.setBigDecimal(2, amount);
    cs.execute();
} catch (SQLException e) {
    switch (e.getErrorCode()) {
        case 6550:
            // ORA-06550: compilation error in an anonymous block
            // Usually a bug in the dynamic SQL construction
            logger.error("PL/SQL compilation error", Map.of(
                "oraCode",  e.getErrorCode(),
                "message",  e.getMessage()  // Contains line, column, PLS code
            ));
            throw new ApplicationBugException("Dynamic PL/SQL compilation failed", e);

        case 4063:
            // ORA-04063: package body has errors
            // A stored object is INVALID — deployment or DDL issue
            // Do NOT retry — this will not resolve on its own
            logger.error("Database object INVALID — deployment issue", Map.of(
                "oraCode",  e.getErrorCode(),
                "message",  e.getMessage()
            ));
            alertingService.critical("ORA-04063: " + e.getMessage());
            throw new DeploymentException("Database object is invalid", e);

        case 6508:
            // ORA-06508: could not find program unit being called
            // The package or procedure does not exist or is INVALID
            logger.error("Program unit not found", Map.of(
                "oraCode",  e.getErrorCode(),
                "message",  e.getMessage()
            ));
            alertingService.critical("ORA-06508: " + e.getMessage());
            throw new DeploymentException("Program unit not found", e);

        default:
            throw e;
    }
}


============================================================
PYTHON / python-oracledb
============================================================

import oracledb

try:
    cursor.callproc('billing_pkg.process_payment', [account_id, amount])
except oracledb.DatabaseError as e:
    error = e.args[0]
    if error.code == 6550:
        # Compilation error in dynamic PL/SQL
        logger.error('PL/SQL compilation error', extra={
            'ora_code': error.code, 'message': error.message})
        raise ApplicationBugError('Dynamic PL/SQL failed') from e
    elif error.code in (4063, 6508):
        # Database object is INVALID — escalate immediately
        logger.error('Database object INVALID', extra={
            'ora_code': error.code, 'message': error.message})
        alert_team('critical', f'ORA-{error.code}: {error.message}')
        raise DeploymentError('Invalid database object') from e
    else:
        raise


============================================================
NODE.JS / node-oracledb
============================================================

try {
    await connection.execute(
        'BEGIN billing_pkg.process_payment(:id, :amt); END;',
        { id: accountId, amt: amount }
    );
} catch (err) {
    if (err.errorNum === 6550) {
        console.error(JSON.stringify({
            level: 'error', oraCode: err.errorNum,
            message: err.message, context: 'dynamic PL/SQL'
        }));
        throw new ApplicationBugError('Dynamic PL/SQL failed', { cause: err });
    } else if ([4063, 6508].includes(err.errorNum)) {
        console.error(JSON.stringify({
            level: 'critical', oraCode: err.errorNum,
            message: err.message
        }));
        await alertingService.critical(`ORA-${err.errorNum}: ${err.message}`);
        throw new DeploymentError('Invalid database object', { cause: err });
    } else {
        throw err;
    }
}


============================================================
Key points:
============================================================
1. ORA-06550: compilation error in dynamic PL/SQL — usually a code bug
2. ORA-04063: stored object is INVALID — deployment/DDL issue, escalate
3. ORA-06508: program unit not found — object missing or INVALID, escalate
4. Do NOT retry ORA-04063 or ORA-06508 — they will not self-resolve
5. Classify ORA-04063 as a deployment error, not an application error
6. Log the full Oracle error message — it contains the INVALID object name
🔥Application Error Classification for Compilation Failures
ORA-06550 from a dynamic anonymous block is an application code bug — the block has a syntax or semantic error. ORA-04063 and ORA-06508 are deployment or DDL issues — a stored object is INVALID and needs recompilation by the database team. Do not conflate them. Classify ORA-04063 and ORA-06508 as infrastructure alerts, not application errors. They should trigger immediate escalation to the database or deployment team, not a code review.
📊 Production Insight
Application teams that treat ORA-04063 as an application bug spend hours reading their own code. The bug is not in the application — it is in the database object that was deployed INVALID. The fix is a GRANT, a recompilation, or a corrected deployment script. Route ORA-04063 and ORA-06508 to the database team immediately.
🎯 Key Takeaway
ORA-06550 in a dynamic block is an application bug. ORA-04063 and ORA-06508 are deployment issues. Do not retry them — they will not self-resolve. Escalate ORA-04063 immediately to the database or deployment team. Log the full Oracle error message for the invalid object name.
🗂 PLS Error Code Reference: Categories, Causes, and Fixes
The PLS code after ORA-06550 identifies the specific failure — use it for diagnosis
PLS CodeCategoryTypical CauseResolutionDiagnostic Query
PLS-00103SyntaxMissing semicolon, unmatched parenthesis, invalid keyword, malformed expressionFix the code grammar at the reported line and column — the error message shows what was found and what was expectedN/A — fix directly in source code
PLS-00201SemanticUndeclared variable, missing table, missing synonym, misspelled identifier, object not yet createdDeclare the variable, create the object, create a synonym, or correct the spellingSELECT object_name FROM all_objects WHERE object_name = 'NAME'
PLS-00302SemanticField or method does not exist on the referenced record type, package, or object typeCheck the type definition — the field was renamed, removed, or never existedSELECT text FROM user_source WHERE name = 'TYPE_NAME' AND type IN ('PACKAGE', 'TYPE')
PLS-00306SemanticWrong number or types of arguments in a procedure or function callCompare the call against the procedure specification — match parameter count and typesDESC schema.package to view the specification
PLS-00904PrivilegeMissing EXECUTE or SELECT grant on a referenced object in another schemaGrant the required privilege directly to the compiling schema — role grants do not work for definer's rightsSELECT * FROM dba_tab_privs WHERE grantee = 'SCHEMA' AND table_name = 'OBJECT'
PLS-00905SemanticObject in another schema is INVALID — cannot be referenced until it compilesFix and recompile the referenced object in the other schema firstSELECT status FROM all_objects WHERE owner = 'OTHER' AND object_name = 'NAME'
PLS-00410SemanticDuplicate column name in a record declaration or SELECT INTO target listRename duplicate columns or use aliases in the SELECT listReview the SELECT list for duplicate column names

🎯 Key Takeaways

  • ORA-06550 is a compile-time error — the object is marked INVALID and nothing executes until it is fixed
  • The PLS error code after ORA-06550 is the actual diagnosis: PLS-00103 = syntax, PLS-00201 = missing identifier, PLS-00904 = missing privilege
  • CREATE OR REPLACE silently marks objects INVALID on compilation failure — always validate with USER_ERRORS, SHOW ERRORS, or ALTER COMPILE
  • A single compilation failure cascades through the entire dependency tree — callers see ORA-04063, not ORA-06550
  • PLS-00904 is a privilege error that looks like a code bug — check dba_tab_privs before debugging the procedure body
  • Definer's rights packages require direct grants — role-based grants are invisible during compilation
  • Run UTL_RECOMP.RECOMP_SERIAL after fixing the root cause to force immediate revalidation of all INVALID dependents

⚠ Common Mistakes to Avoid

    Trusting CREATE OR REPLACE success as confirmation that the object compiled correctly
    Symptom

    Deployment script completes without error. Object is marked INVALID in USER_OBJECTS. Dependent procedures begin failing with ORA-04063 when first called. No error appears in the deployment log.

    Fix

    Always validate after CREATE OR REPLACE. Use SHOW ERRORS in SQL*Plus, query USER_ERRORS in programmatic scripts, or use ALTER COMPILE which raises on failure. Add a post-deployment procedure that checks USER_OBJECTS for INVALID status.

    Fixing only the first compilation error and assuming subsequent errors are related
    Symptom

    After fixing the first PLS error and recompiling, a new error appears at a different line. Engineers think the fix broke something else. In reality, Oracle reports one error per compilation — subsequent errors were always there but could not be reported until the first was resolved.

    Fix

    Fix the first error, recompile, and check for the next error. Repeat until USER_ERRORS returns zero rows for the object. Each error is independent — fixing one does not cause or resolve others.

    Debugging PLS-00904 as a code error instead of checking grants
    Symptom

    Engineers spend hours reviewing syntactically correct code that references a valid object. The compilation still fails with PLS-00904. The object exists, the name is correct, but the compiling schema lacks the EXECUTE or SELECT grant.

    Fix

    Query dba_tab_privs for the compiling schema and the referenced object. If the direct grant is missing, issue it: GRANT EXECUTE ON schema.object TO compiling_schema. For definer's rights packages, role-based grants do not satisfy PLS-00904.

    Deploying to a core package without checking the dependency tree first
    Symptom

    A single compilation error in a core utility package cascades to 47 dependent objects across three schemas. Multiple teams report failures simultaneously. The blast radius was not anticipated and the rollback plan was not ready.

    Fix

    Before deploying to any package that other objects depend on, query USER_DEPENDENCIES to estimate the blast radius. Deploy during low-traffic windows when the blast radius is large. Have a rollback script ready. Validate compilation immediately after deployment.

    Using role-based grants for cross-schema references in definer's rights packages
    Symptom

    The EXECUTE privilege exists through a role. The DBA confirms the role is granted. Compilation still fails with PLS-00904. The code compiles when run by a DBA (who has direct privileges) but fails when compiled by the application schema.

    Fix

    Issue a direct GRANT to the compiling schema: GRANT EXECUTE ON other_schema.pkg TO compiling_schema. Roles are ignored during compilation of definer's rights packages. Alternatively, create the package with AUTHID CURRENT_USER — but this changes the execution privilege model for all callers.

    Not running UTL_RECOMP after fixing a compilation error
    Symptom

    The base object compiles successfully but dependent objects remain INVALID until they are first called. The first call to each dependent incurs a recompilation latency. In production, this causes unpredictable response times for the first batch of requests after a fix.

    Fix

    After fixing the root cause and recompiling the base object, run UTL_RECOMP.RECOMP_SERIAL to force immediate revalidation of all INVALID objects. Then verify: SELECT COUNT(*) FROM user_objects WHERE status = 'INVALID' — should return zero.

Interview Questions on This Topic

  • QWhat is ORA-06550 and how does it differ from runtime errors like ORA-01403?JuniorReveal
    ORA-06550 is a compile-time PL/SQL error that occurs when the Oracle compiler encounters a syntax or semantic issue. It prevents execution entirely — the object is marked INVALID and nothing runs. Runtime errors like ORA-01403 occur during execution after successful compilation. ORA-06550 includes a line number, column number, and a PLS error code that identifies the exact failure location and category. The key distinction: ORA-06550 is caught before any data is read or written, while ORA-01403 occurs during query execution. ORA-06550 errors are always deterministic — they reproduce on every compilation attempt until fixed.
  • QWhy does CREATE OR REPLACE not raise an exception when compilation fails, and what should you do about it?Mid-levelReveal
    Oracle designed CREATE OR REPLACE to allow creating invalid objects to support forward references and circular dependencies during development. Two packages that reference each other cannot both be valid during creation. CREATE OR REPLACE accommodates this by silently marking the object INVALID and completing successfully. For deployment scripts, this is dangerous because the script reports success while deploying broken code. The fix is to never trust CREATE OR REPLACE success. Always follow it with SHOW ERRORS in SQL*Plus, query USER_ERRORS in programmatic scripts, or use ALTER COMPILE which raises on failure. Add a post-deployment health check that queries USER_OBJECTS for any INVALID status.
  • QHow does ORA-06550 cascade to ORA-04063 in production, and how do you diagnose the root cause?Mid-levelReveal
    When a PL/SQL object fails compilation, Oracle marks it INVALID. Every object that depends on it — directly or transitively — is also marked INVALID. When a caller tries to invoke any of these INVALID dependents, it sees ORA-04063: package body has errors. The root cause is the original ORA-06550 in the base object, not the ORA-04063 in the dependent. To diagnose: query USER_OBJECTS WHERE status = 'INVALID' to find all affected objects. Then query USER_ERRORS to find which object actually has compilation errors — that is the root cause. Fix it, recompile it, and run UTL_RECOMP.RECOMP_SERIAL to revalidate all dependents.
  • QWhat is PLS-00904 and why does it require a different debugging approach than other PLS errors?SeniorReveal
    PLS-00904 (insufficient privilege to access object) is a semantic compilation error that fires when the compiling schema lacks a direct grant on a referenced object. It requires different debugging because the code is correct and the object exists — the problem is in the privilege model, not the source code. Engineers waste time reviewing code when the fix is a single GRANT statement. The critical subtlety: definer's rights PL/SQL (the default) requires direct grants — role-based grants are invisible during compilation. If the EXECUTE privilege is granted through a role, PLS-00904 still fires. The fix is always GRANT EXECUTE ON schema.object TO compiling_schema, or converting the package to AUTHID CURRENT_USER which can use role-based grants.
  • QHow would you design a CI/CD pipeline to prevent ORA-06550 from reaching production?SeniorReveal
    Three layers. First: a CI step that deploys all PL/SQL objects to a staging schema that mirrors the production grant model. After deployment, run UTL_RECOMP.RECOMP_SERIAL and query USER_OBJECTS for any INVALID status. Fail the pipeline if any object is INVALID. This catches both syntax errors and privilege errors that only manifest in the production grant model. Second: in the deployment script itself, add SHOW ERRORS after every CREATE OR REPLACE (for SQL*Plus) or query USER_ERRORS programmatically. Abort the deployment on the first error. Third: a post-deployment health check procedure that runs immediately after the script completes, queries USER_OBJECTS for INVALID status, and raises an exception if any are found. The pipeline should not mark the deployment as successful until this check passes with zero INVALID objects.

Frequently Asked Questions

What is the difference between ORA-06550 and ORA-04063?

ORA-06550 is the original compilation error — it occurs when the PL/SQL compiler encounters a syntax or semantic issue. ORA-04063 is the error that callers see when they try to invoke an object that is INVALID due to a prior compilation failure. ORA-06550 contains the line, column, and PLS error code. ORA-04063 contains only the invalid object name. To find the root cause of ORA-04063, query USER_ERRORS for the object named in the message.

Why does CREATE OR REPLACE not raise an error when compilation fails?

Oracle allows creating invalid objects to support forward references and circular dependencies during development. Two packages that reference each other cannot both be valid during creation. CREATE OR REPLACE accommodates this by completing silently and marking the object INVALID. This is safe during development but dangerous in deployment scripts. Always validate compilation after CREATE OR REPLACE by checking USER_ERRORS.

How do I find all INVALID objects in my schema?

Query USER_OBJECTS: SELECT object_name, object_type FROM user_objects WHERE status = 'INVALID' ORDER BY object_type, object_name. For each INVALID object, query USER_ERRORS to find its compilation errors: SELECT line, position, text FROM user_errors WHERE name = 'OBJECT_NAME' ORDER BY sequence. Objects that appear in USER_OBJECTS as INVALID but have no rows in USER_ERRORS were invalidated by a dependency — fix their dependency first.

Can I use role-based grants for PL/SQL compilation?

Only for invoker's rights procedures (AUTHID CURRENT_USER). The default PL/SQL authorization model is definer's rights, which checks only direct grants during compilation — role-based grants are invisible. If PLS-00904 appears despite a role grant, issue a direct grant: GRANT EXECUTE ON schema.object TO compiling_schema.

How do I recompile all INVALID objects after fixing a compilation error?

Use UTL_RECOMP.RECOMP_SERIAL — it recompiles all INVALID objects in the correct dependency order. Alternatively, DBMS_UTILITY.COMPILE_SCHEMA(schema => USER, compile_all => FALSE) recompiles only INVALID objects in the specified schema. After recompilation, verify: SELECT COUNT(*) FROM user_objects WHERE status = 'INVALID' — should return zero.

What does PLS-00103 mean and how do I fix it?

PLS-00103 means the compiler encountered an unexpected token — a syntax error. The error message shows both what was found and what was expected. For example: 'Encountered the symbol BEGIN when expecting one of the following: ;' means a semicolon is missing before the BEGIN keyword. Fix by correcting the grammar at the reported line and column. The most common causes are missing semicolons, unmatched parentheses, and invalid keywords.

🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousORA-01422: Exact Fetch Returned More Than Requested Number of RowsNext →ORA-00942: Table or View Does Not Exist – Full Troubleshooting
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged