Alhamdulillah, sharing another real troubleshooting session — this one from an EBS 12.2 database tier clone on Oracle Linux 8.10, where adcfgclone.pl dbTechStack failed and the on-screen error pointed in completely the wrong direction.
Environment
Oracle EBS 12.2, Oracle Database 19c (19.21) multitenant (CDB/PDB), Oracle Linux 8.10, target server testserver01.
The Symptom
perl adcfgclone.pl dbTechStack /u01/EBSDB/19.0.0/appsutil/EBSDB_testserver01.xml
RC-00110: Fatal: Error occurred while relinking of ApplyDBTechStack
ERROR while running Apply...
ERROR: Failed to execute /u01/EBSDB/19.0.0/appsutil/clone/bin/adclone.pl
The first instinct with RC-00110 is to suspect a relink problem — missing OS packages, a corrupt backup, bad extraction. We verified all of that and ruled it out. The real lesson of this post: the RC error on screen is generic. Follow the log chain.
The Log Chain
Step 1 — The ApplyDBTechStack log showed the relink script actually passed:
adlnkoh.sh completed sucessfully
but home registration failed:
Finished OUI CLI cloning for s_db_oh with return code: 1
ouicli.pl INSTE8_APPLY 1
RC-50013: Fatal: Instantiate driver did not complete successfully.
Step 2 — ohclone.log pointed one level deeper:
OUI runinstaller log file - /u01/EBSDB/oraInventory/logs/InstallActions<timestamp>/installActions<timestamp>.log
Found the INFO: Exit Status is -1 in runInstaller log.
OUI CLI cloning returned non-zero.
Note the timing: runInstaller started and exited within the same second. An installer that dies instantly is not failing a task — it is failing a pre-check.
Step 3 — The installActions log had the true error:
[WARNING] [INS-08101] Unexpected error while executing the action at state: 'supportedOSCheck'
SUMMARY: - java.lang.NullPointerException
Root Cause
The 19c ORACLE_HOME delivered inside the EBS Rapid Clone stage carries the 19.3 base installer (year 2019). Its OS certification table ends at Oracle Linux 7. On OL8/RHEL8, the lookup for the distribution ID returns null and the installer crashes with a NullPointerException before doing any work — surfacing back up the chain as RC-50013 and RC-00110.
The Fix
Set CV_ASSUME_DISTID in the same shell session before rerunning the clone:
export CV_ASSUME_DISTID=OEL7.8
echo $CV_ASSUME_DISTID
cd /u01/EBSDB/19.0.0/appsutil/clone/bin
perl adcfgclone.pl dbTechStack /u01/EBSDB/19.0.0/appsutil/EBSDB_testserver01.xml
Result — the rerun completed cleanly, confirmed by these lines in the new ApplyDBTechStack log:
Finished OUI CLI cloning for s_db_oh with return code: 0
Completed home registration for s_db_oh
Completed Apply...
ApplyDBTechStack Completed Successfully.
Important: this variable does NOT change your OS or the installed software. It only tells the old installer which certification profile to use for its checks. The value must be one the 19.3-base installer recognizes — OEL7.8 is the value documented in Oracle’s Linux 8 release notes. Setting it to your actual OS version (8.10) defeats the purpose.
Permanent Fix
The workaround can also live inside the ORACLE_HOME, in $ORACLE_HOME/cv/admin/cvu_config:
# Fallback to this distribution id
CV_ASSUME_DISTID=OEL7.8 <-- uncomment and set
This also explains a common confusion: “my previous clone worked without this!” If the earlier backup was taken from a home where cvu_config already had this set, the fix travelled inside the backup. A backup from an untouched home reintroduces the failure. Check with:
After the fix, you will still see FATAL errors like “DB Connection failed” and “Invalid APPS database user credentials” from the AutoConfig phase, plus DB-ETCC connectivity warnings — these are expected at this stage because the database is not restored and opened yet. They resolve once you restore the database and rerun AutoConfig and ETCC.
Also refresh ETCC from patch 17537119 — the bundled bugfix XML goes stale after 30 days, and older versions do not recognize newer Release Updates such as 19.21.
Key Takeaways
RC-00110/RC-50013 are wrappers, not root causes — always walk ApplyDBTechStack log → ohclone.log → installActions log. A runInstaller that exits in under a second failed a pre-check, not the work itself. And on OL8/RHEL8 with any 19.3-base home, CV_ASSUME_DISTID=OEL7.8 belongs in your clone runbook.
Disclaimer: The views expressed on this blog are my own and do not reflect the views of my employer or any client. All environment names, hostnames, and identifiers used in this post are anonymized. Always test in a non-production environment before applying any change to production.
Alhamdulillah, another interesting production incident to share. This one is a classic example of how a small code change — a single commented-out COMMIT — can stay silent for weeks until a new concurrent workload exposes it as an Oracle deadlock in production. If you are searching for Oracle deadlock troubleshooting or ORA-00060 trace file analysis in an Oracle EBS environment, this walkthrough covers the full investigation from symptom to fix.
Quick Summary
Issue: ORA-00060 deadlocks after deployment of a new APEX DBMS_SCHEDULER job.
Impact: Oracle Workflow Background Process intermittently failed, delaying order processing.
Root Cause: A previously removed COMMIT caused row locks to be held far longer than intended, allowing concurrent sessions to deadlock.
Resolution: Restored the correct transaction boundary, adjusted scheduler timing, and verified with Oracle Support that no product-side issue was involved.
Result: No recurrence after deployment.
The Symptom
On a production EBS environment (EBS 12.1, Database 12.1.0.2), we started receiving alerts for ORA-00060 in the database alert log:
ORA-00060: Deadlock detected. More info in file
/u01/app/oracle/diag/rdbms/prod/PROD/trace/PROD_ora_12345.trc
At the same time, the business reported sales order lines not progressing. The Oracle Workflow Background Process for the OM Order Line workflow (ONT) was erroring intermittently — a visible impact on order processing and a classic Oracle EBS performance troubleshooting scenario.
A quick note on deadlock behavior: Oracle detects deadlocks automatically and resolves them by rolling back one of the participating statements — the victim — allowing the remaining transaction to continue. Unlike simple blocking, only the offending statement is rolled back, not the victim’s entire transaction. The victim’s session receives ORA-00060 and a trace file is written to disk. That is why the application does not hang forever; instead, you see intermittent errors and trace files accumulating.
Step 1 – Read the Deadlock Trace
The trace file is always the starting point of ORA-00060 trace file analysis. The deadlock graph showed two sessions blocking each other on TX enqueues in row-exclusive mode:
Deadlock graph:
---------Blocker(s)-------- ---------Waiter(s)---------
Resource Name process session holds waits process session holds waits
TX-000a0015-0003c2d1 45 312 X 38 127 X
TX-0008001f-0004a1b2 38 127 X 45 312 X
Both sessions were waiting on enq: TX - row lock contention, each holding a row the other wanted — textbook Oracle row lock contention forming a deadlock cycle. The trace file also identifies which session was chosen as the victim and the exact SQL statement that was rolled back, which makes it the definitive evidence for the investigation.
Conceptually, the cycle looked like this:
Workflow Background Process
│
│ Locks Row A
▼
Reservation Row A
│
│ Waiting for Row B
▼
Reservation Row B
▲
│ Locked by
│
APEX Scheduler Job
Workflow waits for APEX.
APEX waits for Workflow.
→ Oracle detects a deadlock (ORA-00060)
Step 2 – Identify the Two Sessions
SET LINES 200 PAGES 100
COL SID FORMAT 99999
COL SERIAL# FORMAT 999999
COL USERNAME FORMAT A12
COL PROGRAM FORMAT A28
COL MODULE FORMAT A28
COL ACTION FORMAT A20
COL SQL_ID FORMAT A14
SELECT s.sid, s.serial#, s.username,
NVL(s.program,'N/A') program,
NVL(s.module,'N/A') module,
NVL(s.action,'N/A') action,
NVL(s.sql_id,'N/A') sql_id
FROM v$session s
WHERE s.sid IN (312, 127);
Session 1 was the Workflow Background Process (FNDWFBG, ONT item type) — making this an Oracle Workflow deadlock scenario. Session 2 was more interesting — a DBMS_SCHEDULER job session:
COL JOB_NAME FORMAT A25
COL OWNER FORMAT A12
COL STATE FORMAT A12
COL REPEAT_INTERVAL FORMAT A40
SELECT owner, job_name, state, repeat_interval
FROM dba_scheduler_jobs
WHERE job_name = 'APEX_ORDER_RESERVE';
A newly deployed APEX-driven scheduler job, running every few minutes, calling a custom reservation package — the second half of an Oracle DBMS_SCHEDULER deadlock pattern.
Step 3 – Which Rows Were They Fighting Over?
From the trace file, the “Rows waited on” section gives the object number:
COL OWNER FORMAT A10
COL OBJECT_NAME FORMAT A30
COL OBJECT_TYPE FORMAT A12
SELECT owner, object_name, object_type
FROM dba_objects
WHERE object_id = &object_id_from_trace;
Both sessions were colliding on reservation rows — the Workflow process updating them as part of order line progression, and the APEX job updating the same rows through the custom package.
Step 4 – The Root Cause
Reviewing the custom reservation package source, we found this:
UPDATE xx_order_reservations
SET status = 'RESERVED'
WHERE header_id = p_header_id;
-- COMMIT; <== commented out during a previous change
The investigation identified that the custom package was holding row locks far longer than intended, because a COMMIT had been removed in an earlier code change. Instead of releasing locks per iteration, the scheduler job session held all of its row locks across the entire loop over order headers. It is worth being precise here: a missing COMMIT by itself does not cause a deadlock — a deadlock requires two sessions acquiring locks on overlapping rows in conflicting order. What the missing COMMIT did was dramatically widen the lock-holding window, so when the new APEX scheduler job started running concurrently with the Workflow Background Process against the same reservation rows, the probability of the two sessions interleaving into a deadlock cycle went from negligible to near-certain.
This also explains why the package ran in production for weeks without issue — until the APEX job was deployed, nothing else contended for those rows at that frequency. It also explains why testing never caught it: lower environments rarely generate the same level of concurrent activity as production, making lock-contention issues extremely difficult to reproduce before go-live. The extended lock duration was always present; the new concurrent workload exposed it.
The Fix
Restored the COMMIT at the correct transactional boundary in the custom package (per-iteration, after each header’s reservation update), shrinking the lock-holding window.
Redeployed the package during an approved change window.
Rescheduled the APEX job to avoid peak Workflow Background Process cycles as an additional safety margin.
Raised an SR with Oracle Support to confirm no product-side involvement — confirmed clean; purely custom code.
No ORA-00060 recurrence since the fix, ما شاء الله.
Lessons Learned
An Oracle deadlock almost always has two contributors: the locking pattern AND the concurrency pattern. Fixing either breaks the cycle, but fix the code defect, not just the schedule.
Commented-out COMMITs are silent time bombs. Code review for custom PL/SQL touching EBS transactional tables must treat transaction boundaries as seriously as the DML itself.
New scheduler jobs (APEX, DBMS_SCHEDULER, concurrent programs) should be assessed for row-level contention with existing Oracle Workflow and concurrent processing before go-live.
The deadlock trace file gives you everything: the sessions, the victim, the SQL, the rows. Start there, not with guesswork.
Conclusion
Production incidents often reveal issues that remain hidden during testing, because realistic concurrency is difficult to reproduce in lower environments. This incident reinforced a simple discipline: whenever a new workload is introduced — an APEX scheduler job, a concurrent program, or an integration — review the transaction boundaries of every custom PL/SQL object it touches, and ask what else updates those same rows.
In Oracle, deadlocks are rarely caused by a single statement — they are caused by the interaction of multiple sessions under concurrency. Understanding transaction boundaries is often the key to solving them.
Disclaimer: All environment names, hostnames, and identifiers in this post are anonymized. The views expressed are my own.
Alhamdulillah, this post brings together one of the more involved upgrade journeys I have worked through recently — taking an Oracle E-Business Suite 12.2 database stack from 12.1.0.2 all the way to 19c, on a single standalone host running Oracle Restart.
What made this engagement worth writing up is that it was not a clean, textbook upgrade. The Grid Infrastructure layer presented several unexpected challenges — a chain of CLSRSC failures that had to be cleared before it would move, and once Grid was finally on 19c, the database upgrade and the non-CDB to PDB conversion had their own set of gotchas around ASM registration, password files, and UTL_FILE_DIR.
I have combined both phases — the Grid Infrastructure upgrade and the database upgrade plus multitenant conversion — into one walkthrough, because in the real world they are not separate stories. The GI upgrade forms the foundation for every subsequent phase. If you only read GI upgrade blogs in isolation, you miss how the pieces connect.
All hostnames, database names, ASM diskgroup names, and paths below are genericized. The technical substance is exactly as it played out.
Published: June 2026
Environment: Oracle EBS 12.2.4 | AD/TXK Delta 13 | Oracle Database 19c (19.30) (Versions shown are from the environment used in this engagement, not a minimum certified baseline — always confirm your own certified levels.)
Executive Summary
This upgrade involved moving an Oracle E-Business Suite 12.2 database environment from Oracle Database 12.1.0.2 to 19c on a standalone Oracle Restart host. The journey consisted of four major phases:
Oracle Restart (Grid Infrastructure) upgrade from 12.1.0.2 to 19c
Database upgrade from 12.1.0.2 to 19c using DBUA
Non-CDB to PDB conversion using Oracle EBS TXK utilities
EBS application-tier reconfiguration and validation
The most time-consuming portions were not the upgrades themselves, but resolving legacy Oracle Restart configuration issues, rebuilding HAS components, handling ASM registration after the GI upgrade, addressing password-file requirements during the PDB conversion, and resolving UTL_FILE_DIR migration challenges.
This guide covers an end-to-end Oracle EBS 12.2.4 upgrade to Oracle Database 19c, including Oracle Restart upgrade, DBUA upgrade, non-CDB to PDB conversion, AutoConfig remediation, and post-upgrade validation.
Environment overview
Component
Detail
Platform
Single standalone Linux host (dbhost) running Oracle Restart / standalone HAS — not full RAC Clusterware
Apps tier
Separate node (appshost) under the applmgr-style owner
EBS release
12.2.4
AD level
AD.C.Delta.13
TXK level
TXK.C.Delta.13
Source GI
12.1.0.2
Target GI
19c (19.30)
Source DB
Non-CDB EBS database, SID ebsdb, on 12.1.0.2
Target DB
19c (19.30), ebsdb plugged in as a lowercase PDB inside CDB EBSCDB
The apps tier and ASM storage stay in place; everything between them moves up to 19c, and the standalone non-CDB becomes a pluggable database inside a container.
A note on certification before you begin
Do not assume any upgrade path is valid. Before starting, confirm that your EBS code level, AD/TXK delta level, database RU, and interoperability patches are certified per the Oracle EBS 12.2 and Oracle Database 19c certification and interoperability documentation. Skipping this check is the most common reason an upgrade that “worked in the lab” falls apart in the field. Database RU levels and EBS interoperability certifications change frequently, so always verify the latest certification matrix in My Oracle Support before implementation — and treat the 19.30 RU referenced here as the level used in this engagement, not the only supported target.
Rough timeline (for maintenance-window planning)
Phase
Typical duration
GI (Oracle Restart) upgrade
30–60 min
DBUA database upgrade
2–4 hrs
Non-CDB → PDB conversion
~15 min
AutoConfig & validation
30–60 min
These are working estimates from this engagement, not guarantees — database size, invalid-object count, and hardware all move the numbers. Treat the GI and DBUA phases as the long poles when you size the window.
Phase 1 — Grid Infrastructure 12.1.0.2 → 19c
The 19c Grid software was already laid down (gold image extracted into the new home), and the OUI installer had paused — as it always does — waiting for rootupgrade.sh to be run as root. That is where everything went sideways.
The first wall: CLSRSC-324 — Could not open old init.cssd
Running the upgrade script:
/u01/app/19.0.0/grid/rootupgrade.sh
failed almost immediately:
CLSRSC-324: Could not open old init.cssd
Died at /u01/app/19.0.0/grid/crs/install/s_crsutils.pm line 1569
The 19c rootupgrade.sh reads the old configuration from init.cssd to learn where the previous CRS home lived. On this host, /etc/init.d/init.cssd was simply missing — it had been stripped out during earlier deconfig attempts before I picked the task up.
First I confirmed what the upgrade actually expected as the old home:
The third major blocker: CLSRSC-348 / CLSRSC-318 — old HAS stack will not stop or start
With the bootstrap files back, rootupgrade.sh progressed to step 2 (GetOldConfig) and then stopped again:
CLSRSC-192: Unable to stop Oracle Restart
CLSRSC-348: The Oracle Restart stack failed to stop
The upgrade was trying to cleanly stop the old 12.1 HAS stack — but the old stack was not even contactable:
/u01/app/12.1.0.2/grid/bin/crsctl check has
CRS-4639: Could not contact Oracle High Availability Services
So I tried to start it, and hit the classic library-mismatch error:
/u01/app/12.1.0.2/grid/bin/crsctl start has
CRS-4652: Failure 3 in clsvswversion for the local node
Root Cause
The original 12.1 Oracle Restart stack was not in a healthy state. Previous deconfiguration attempts had removed critical bootstrap files and left HAS only partially configured.
Because Oracle only upgrades an existing, functioning stack, rootupgrade.sh could not proceed until the old stack was cleanly rebuilt and verified. The fix is to deconfigure it fully, rebuild it on 12.1, confirm it is online — and only then let the 19c upgrade take over.
Rebuild the old 12.1 HAS stack using the 12.1 perl binary (do not use the system perl — use the one shipped with the old Grid home):
/u01/app/12.1.0.2/grid/perl/bin/perl \
-I/u01/app/12.1.0.2/grid/perl/lib \
-I/u01/app/12.1.0.2/grid/crs/install \
/u01/app/12.1.0.2/grid/crs/install/roothas.pl
This reconfigured Oracle Restart successfully on 12.1 — note this is the script rebuilding the old stack, confirmed by:
CRS-4123: Oracle High Availability Services has been started.
CLSRSC-327: Successfully configured Oracle Restart for a standalone server
Verify HAS is genuinely online before going further:
/u01/app/12.1.0.2/grid/bin/crsctl check has
CRS-4638: Oracle High Availability Services is online
Clearing stale checkpoints and finally running the upgrade
The earlier failed rootupgrade.sh runs leave behind checkpoint files that block any retry — the script thinks it has already partly completed and refuses to start fresh. Clear them:
This time it walked cleanly through all 12 steps (10–20 minutes), pinned the node, backed up the OLR at both the 12.1 and 19c homes, and finished with:
CRS-4123: Oracle High Availability Services has been started.
CLSRSC-327: Successfully configured Oracle Restart for a standalone server
Back in the OUI GUI, clicking OK finalized the installer. ما شاء الله — Grid was now on 19c.
One of the most common post-GI issues: ASM was never registered
This one is worth its own warning, because it does not surface during the GI upgrade — it surfaces later, when you go to create the CDB and DBCA fails with something like DBT-06604: insufficient free space. The real reason is that ASM was not registered with Oracle Restart, so the tools silently fell back to filesystem storage, which did not have the room.
A point worth being precise about: depending on the configuration, ASM may already exist and simply not be running — in which case srvctl start asm (or mounting the diskgroups) is all that is needed. Check before you add — running srvctl add asm when ASM is already configured will error, so confirm the current state with srvctl status asm / srvctl config asm first. In this environment ASM was not registered correctly, so it had to be added back:
# verify first — does Oracle Restart already know about ASM?
srvctl status asm
srvctl config asm
# in this environment it was not registered, so:
srvctl add asm
srvctl start asm
And if the diskgroups are not mounted, mount them explicitly from the ASM instance:
ALTER DISKGROUP DATA MOUNT;
ALTER DISKGROUP RECO MOUNT;
Lesson: after any Oracle Restart GI upgrade, always confirm srvctl status asm before you trust any DBCA/DBUA storage decision.
Lessons Learned
Verify Oracle Restart is healthy before attempting an upgrade.
Confirm init.cssd and olr.loc exist and point to valid locations.
Clear stale checkpoint files before retrying rootupgrade.sh.
Validate ASM registration immediately after the upgrade.
Phase 2 — Database 12.1.0.2 → 19c via DBUA
With 19c Grid and ASM healthy, and the 19c CDB (EBSCDB) already created and patched (datapatch + catmgd.sql for the MGDSYS schema), the next phase was upgrading the non-CDB ebsdb in place from 12.1.0.2 to 19c. It stays a non-CDB for now — the PDB conversion is a deliberately separate Phase 3.
For EBS environments, launch DBUA using the -keepEvents option so that the EBS-required events and hidden (underscore) parameters are preserved during the upgrade. Dropping those silently breaks the apps tier later.
X11 forwarding across a user switch
A very common, very annoying blocker: you SSH in as one OS user, then sudo to oracle, and X11 forwarding breaks because DISPLAY and the Xauthority cookie do not carry across the user switch. DBUA is a GUI tool, so this stops you cold.
The fix is to capture the cookie as your login user and add it under oracle:
# as the login user
xauth list # note the cookie line for your display, e.g. dbhost/unix:11 MIT-MAGIC-COOKIE-1 <cookie>
xdpyinfo | head # must return display info, not "unable to open display"
Tip: do not reuse the runbook author’s DISPLAY value (their own workstation name). It will never work for you — set your own.
DBUA wizard choices that matter for EBS
Screen
Choice
CDB / PDB conversion
Leave unchecked — conversion is a separate later phase
Recovery option
“I have my own backup/restore strategy” (RMAN backup already taken) — never leave this blank
Recompile invalid objects
Enabled (parallel)
Upgrade timezone data
Enabled
compatible
Leave at 12.1.0.2 during the upgrade — preserves rollback. Raise it later, deliberately.
Listener migration
Keep the CDB listener migration checked
A couple of things that look like errors but are not:
The DBUA summary may show db_unique_name where you expect db_name. If your unique name differs from the SID, this is just DBUA displaying the unique name — verify against the live DB and move on.
The CDB listener (here on port 1523) must be up before DBUA can pass the network screen. If it is down, start it from the 19c home first: lsnrctl start <listener_name>.
Wrap the whole thing in screen (or tmux) so an SSH drop does not kill the upgrade mid-flight. If screen is not installed, at minimum use nohup-style protection — a dropped DBUA mid-upgrade is a bad day.
DBUA for an EBS 12.1.0.2 → 19c database typically runs 2–4 hours, plus 30–60 minutes of recompile/datapatch. Expect some ORA- noise in the logs (recompile warnings); watch for new error patterns, not the routine ones.
Post-DBUA housekeeping
Once the upgrade completes and all post-upgrade validation is complete (still as a non-CDB), raise COMPATIBLE to 19.0.0. Note that after COMPATIBLE is increased, rollback to the previous release is no longer possible — so this is a deliberate post-validation step, not an automatic next one:
-- raise compatibility — this is the point of no return, do it deliberately
ALTER SYSTEM SET compatible='19.0.0' SCOPE=SPFILE;
-- compatible only takes effect after a restart
SHUTDOWN IMMEDIATE;
STARTUP;
The exact value depends on your site standards and Oracle’s recommendation for the release — some shops set 19.0.0, others use the fully-qualified 19.0.0.0.0. Both forms are accepted; confirm which one your organization standardizes on before setting it.
Then:
Snapshot a pfile from the spfile.
Relocate the spfile into the 19c home’s dbs/.
Remove obsolete parameters that 19c rejects — notably sec_case_sensitive_logon and utl_file_dir.
Run the XDB migration scripts: dbmsxdbschmig.sql then prvtxdbschmig.plb.
Run utlrp.sql (twice) to recompile. In this environment the invalid count dropped from over 12,000 to under 30 across the two passes — normal for an EBS database.
At this point ebsdb is a healthy 19c non-CDB, ready to be plugged in.
Lessons Learned
Always launch DBUA with -keepEvents for EBS — it preserves the required underscore parameters and event settings.
Fix X11 forwarding across the user switch before launching; never reuse the runbook author’s DISPLAY value.
Leave compatible at 12.1.0.2 during the upgrade and raise it deliberately afterward to preserve rollback.
Treat the db_unique_name-vs-db_name display in the summary as cosmetic, and start the CDB listener before the network screen.
Wrap the run in screen/tmux so an SSH drop cannot kill the upgrade mid-flight.
Phase 3 — Non-CDB to PDB conversion
This is where ebsdb becomes a lowercase PDB inside EBSCDB. EBS ships a set of txk driver scripts for exactly this path; the lowercase, case-sensitive PDB name is a detail that bites you in every subsequent command (ALTER SESSION SET CONTAINER="ebsdb" — with the quotes).
This generates the PDB descriptor XML (ebsdb_PDBDesc.xml, the output of DBMS_PDB.DESCRIBE, carrying the <ncdb2pdb>1</ncdb2pdb> flag) and shuts the non-CDB ebsdb down permanently — once you are on this path, there is no casual restart of the old non-CDB.
This reported 8 violations — and per the runbook rules, all were ignorable: standard SQL-patch warnings and the usual benign messages, no character-set mismatch (a character-set violation is the one you must not ignore). Always read each violation; do not blanket-ignore.
Step 3 — Create the PDB (plug in)
perl $AD_TOP/patch/115/bin/txkCreatePDB.pl
This performs a NOCOPY plug-in, allowing the existing ASM datafiles to remain in place while the database is converted into a PDB (the exact ASM file layout varies by environment and Oracle version), and runs noncdb_to_pdb.sql to finish the conversion. It completed in roughly 14 minutes, the PDB state was saved, and ebsdb showed up as CON_ID 3, READ WRITE. ما شاء الله.
This is where the real fights happened. Three blockers, all around password files:
a) ASM password file pre-staging. The script needs the CDB password file in ASM. Pre-stage it with orapwd from the DB home (not the Grid home) into the ASM path for the CDB, then register it:
b) ORA-65066 on the SYSTEM password. Setting the SYSTEM password inside the new PDB threw ORA-65066 until it was applied across all containers:
ALTER USER SYSTEM IDENTIFIED BY "<StrongPwd#1>" CONTAINER=ALL;
c) OPW-00029 complexity failure.orapwd rejected the chosen SYSTEM password with OPW-00029 until it satisfied 19c’s special-character complexity rules. Use a value that genuinely meets the 19c verifier (uppercase + lowercase + digit + special character), e.g. <StrongPwd#1>.
Step 5 — Post-PDB AutoConfig and the UTL_FILE_DIR trap
Running AutoConfig on the DB tier after the conversion initially failed with a UTL_FILE_DIR error. The trace pointed at /usr/tmp being a symlink rather than a real directory, which the new UTL_FILE_DIR-replacement mechanism in 19c does not tolerate.
The fix is to drive the directory configuration explicitly with txkCfgUtlfileDir.pl. First, manually create the directory-list file with real (non-symlink) paths:
# e.g. /u01/app/oracle/product/19.0.0/dbhome_1/dbs/ebsdb_utlfiledir.txt
After that, AutoConfig on the DB tier completed cleanly.
Worth flagging for the longer term: UTL_FILE_DIR is deprecated, and Oracle’s recommended direction is to use database directory objects (CREATE DIRECTORY) for PL/SQL file I/O wherever your EBS code level supports it. The txkCfgUtlfileDir.pl mechanism above is the EBS-supported bridge for this release; treat directory objects as the strategic target.
Lessons Learned
The lowercase, case-sensitive PDB name ("ebsdb" with quotes) propagates into every subsequent ALTER SESSION SET CONTAINER — get it right once and stay consistent.
Read every compatibility violation; ignore only the benign ones, and never ignore a character-set mismatch.
Budget real time for password files: pre-stage the ASM password file from the DB home, expect ORA-65066 (apply CONTAINER=ALL) and OPW-00029 (19c complexity rules).
A symlinked /usr/tmp breaks post-PDB AutoConfig — drive UTL_FILE_DIR explicitly via txkCfgUtlfileDir.pl with real paths.
Phase 4 — Application-tier cutover (overview)
With the database tier healthy at 19c as a PDB, the apps-tier work on appshost is the final phase:
Run AutoConfig on both filesystems (fs1 and fs2) to point the apps tier at the new 19c PDB connection details.
Clean up any stale adop sessions before starting fresh — an abandoned session from days earlier will block a new cycle. Run adop cleanup (cleanup_mode=full) and then a fresh adop phase=prepare.
Validate HugePages, the env files, and Configuration Manager.
I will cover the full apps-tier cutover in detail in a follow-up post, since it deserves its own walkthrough.
Final Validation Checklist
After the upgrade and conversion were complete, the following validations were performed:
✓ Oracle Restart resources online and managed by srvctl ✓ ASM instance and diskgroups mounted ✓ CDB and PDB open in READ WRITE mode ✓ Database listener services registered correctly ✓ AutoConfig completed successfully on database and application tiers ✓ Concurrent Managers started successfully ✓ FNDSM service validated ✓ Internal Monitor running normally ✓ Forms and WebLogic services accessible ✓ adop phase=prepare completed without errors ✓ Invalid objects reviewed and reduced to an acceptable count ✓ Application smoke testing completed ✓ All EBS services registered in the listener ✓ Concurrent Manager processing validated ✓ Test concurrent request submitted and completed ✓ Forms login verified ✓ Workflow Mailer status verified ✓ OACORE, FORMS, and OAFM managed servers healthy ✓ No critical errors in AutoConfig logs ✓ adopscanlog reviewed with no blocking errors ✓ Database and ASM resources managed successfully through srvctl
Quick post-upgrade SQL validation
A handful of SQL checks confirm the database came back healthy — the component registry, the container/PDB state, and the invalid-object count. Set column formatting first so the output stays readable:
1. Component registry — every component should report VALID (or UPGRADED) at 19.0.0:
SET LINESIZE 200 PAGESIZE 100
COL comp_name FORMAT A45
COL version FORMAT A18
COL status FORMAT A12
SELECT comp_name, version, status
FROM dba_registry
ORDER BY comp_name;
2. Container and PDB state — for a 19c multitenant environment, V$PDBS is the most useful check because it shows OPEN_MODE directly (the EBS PDB should be READ WRITE):
COL name FORMAT A20
SELECT con_id, name, open_mode
FROM v$pdbs
ORDER BY con_id;
DBA_PDBS complements it by showing the persisted state — NORMAL for a healthy plugged-in PDB:
COL pdb_name FORMAT A20
COL status FORMAT A12
SELECT pdb_name, status
FROM dba_pdbs
ORDER BY pdb_name;
3. Invalid objects — review the breakdown by owner, then the overall total (an EBS database normally settles to a small count after two utlrp.sql passes):
COL owner FORMAT A22
COL object_type FORMAT A22
SELECT NVL(owner,'(none)') AS owner,
object_type,
COUNT(*) AS invalid_count
FROM dba_objects
WHERE status = 'INVALID'
GROUP BY owner, object_type
ORDER BY invalid_count DESC;
-- overall total
SELECT COUNT(*) AS total_invalid
FROM dba_objects
WHERE status = 'INVALID';
Although every environment is different, the upgrade itself proved relatively straightforward once the underlying Oracle Restart configuration issues were corrected. The majority of effort was spent validating and repairing legacy infrastructure assumptions rather than executing the upgrade utilities themselves.
Key takeaways
A GI upgrade is only as clean as the old stack underneath it. Most CLSRSC-318 / CLSRSC-324 / CLSRSC-348 failures trace back to an old HAS stack that was never properly configured or was half-deconfigured. Rebuild it cleanly on the old version first, then upgrade.
Verify the bootstrap files yourself. Do not assume the OLR is <hostname>.olr — check cdata/localhost/ and verify with ocrcheck -local.
Stale checkpoints block retries silently. Clear ROOTHAS_STACK / ROOTCRS_STACK before re-running rootupgrade.sh.
After an Oracle Restart GI upgrade, register ASM (srvctl add asm / srvctl start asm) before you trust any storage-related tool.
Use dbua -keepEvents for EBS, and fix X11 forwarding across the user switch before you launch.
The PDB conversion’s hardest part is password files — ORA-65066, OPW-00029, and ASM password-file placement. Budget time for it.
Post-PDB AutoConfig + UTL_FILE_DIR: a symlinked /usr/tmp will fail it. Drive the directory list explicitly with txkCfgUtlfileDir.pl using real paths.
Looking back, the actual upgrade steps were straightforward. The real effort was understanding and repairing the assumptions the upgrade process makes about the existing environment. Missing bootstrap files, partially deconfigured Oracle Restart components, ASM registration gaps, password-file placement, and UTL_FILE_DIR migration issues consumed far more time than the upgrade binaries themselves. Those lessons are often absent from official documentation — which is exactly why I wanted to document the journey end to end.
References
Oracle E-Business Suite Release 12.2 — Interoperability Notes for Oracle Database 19c (My Oracle Support)
Oracle Database 19c Upgrade Guide
Using UTL_FILE_DIR or Database Directories for PL/SQL File I/O in Oracle E-Business Suite (My Oracle Support)
Oracle E-Business Suite Multitenant / Pluggable Database (PDB) conversion documentation
Oracle Grid Infrastructure Installation and Upgrade Guide (Oracle Restart)
(Look up the current My Oracle Support note IDs for your exact code and database levels — they are revised regularly.)
If this write-up helped you during your own Oracle E-Business Suite upgrade, you’re welcome to follow the blog for more real-world Oracle Database, EBS, ADOP, cloning, upgrade, and troubleshooting guides drawn from production experience.
Disclaimer: This post reflects my own hands-on experience and is shared for educational purposes only. All hostnames, database names, diskgroup names, and paths have been genericized. Always test thoroughly in a non-production environment and follow Oracle’s official documentation and your organization’s change-control process before applying any of these steps. The views expressed here are my own and do not represent those of any employer or client.
When you finish cloning an Oracle E-Business Suite R12.2 environment, the moment of truth is the login page. You hit the clone’s web entry URL, the page loads, you type your credentials… and the browser quietly throws you onto the production URL. On a multi-node app tier this is more than an annoyance — it means your freshly cloned, supposedly isolated environment is reaching back into PROD, and a careless tester could authenticate against the wrong system entirely.
I ran into exactly this on a recent R12.2.11 clone built on Oracle Cloud Infrastructure: a three-node application tier sharing a single run/patch file system over an FSS (File Storage Service) NFS mount. The clone came up, services started, but every login attempt redirected to the PROD web entry host. The initial investigation uncovered two primary issues — phantom WebLogic managed servers carried over from the source domain, and a missing DNS A-record for the clone’s web entry hostname. Along the way I also identified several other clone-related configuration areas that can produce the same symptom, including stale profile options, OHS configuration remnants, load balancer redirects, and custom code references.
This post walks through how the symptom presents, how to diagnose it cleanly, and the supported way to fix each cause.
Throughout, I use placeholder names — clone-apps.example.com for the clone web entry host, prod-apps.example.com for production, and appnode1/2/3 for the three app-tier nodes. Substitute your own values.
The architecture (why a shared file system matters here)
The clone app tier looked like this:
Three application-tier nodes (appnode1, appnode2, appnode3).
A shared dual file system (fs1 run / fs2 patch) hosted on an OCI FSS NFS export, mounted identically on all three nodes.
A single WebLogic domain (EBS_domain_<SID>) living on that shared file system.
The shared file system is the detail that makes the “phantom managed server” problem sticky. Because config.xml and the managed-server definitions physically live on the shared FSS mount, any leftover managed-server entries from the source domain are visible to every node at once. AutoConfig regenerates context-driven artifacts, but it does not, on its own, delete managed servers that no longer belong to the topology.
The symptom
After adcfgclone completed and the application services started:
The clone login page (https://clone-apps.example.com:<port>/OA_HTML/AppsLogin) rendered correctly.
On submitting credentials — or sometimes immediately on the redirect to the home page — the browser landed on https://prod-apps.example.com/....
The WebLogic Admin Console showed more managed servers than the three-node clone should have, several of them in an unreachable / shutdown state.
Two independent problems reinforced each other: stale host references remained inside the cloned WebLogic topology, while the clone web entry hostname could not be resolved correctly. Either issue can cause redirect anomalies, but together they consistently redirected users back to PROD.
First, rule out the context file
Before investigating WebLogic, verify the clone context file itself doesn’t still contain production hostnames. AutoConfig can only generate correct configuration if the context values are correct, so everything downstream is built from here:
grep -i "prod-apps.example.com" $CONTEXT_FILE
grep -i "prod" $CONTEXT_FILE
If PROD survives in the context, fix it there and re-run AutoConfig before chasing anything downstream — otherwise you’ll be debugging generated artifacts while the source of the bad values sits upstream.
Root cause 1 — Phantom WebLogic managed servers
A correctly provisioned three-node oacore service should have oacore_server1, oacore_server2, oacore_server3 (and the matching oafm, forms, forms-c4ws servers per node). The cloned domain carried extra managed servers that mapped to the source environment’s nodes — servers that pointed at listen addresses and host references belonging to PROD.
These phantom servers do three harmful things:
They keep PROD host references alive inside config.xml.
They confuse the EBS service control and the Admin Server’s view of the cluster.
They can answer (or fail to answer) requests in ways that surface PROD URLs.
Diagnosing it
Inventory what the domain actually contains versus what the topology should be:
# What managed servers does the domain config believe in?
Any managed server or fnd_nodes row that references a host which is not one of appnode1/2/3 is a phantom artifact from the source.
It’s also worth checking the role assignments — sometimes the hostname is correct but node registrations are duplicated or carry the wrong roles:
COL node_name FORMAT a30
COL support_db FORMAT a10
SELECT node_name,
support_db,
support_cp,
support_web,
support_admin
FROM fnd_nodes
ORDER BY node_name;
Fixing it (the supported way)
Do not hand-edit config.xml. EBS R12.2 ships a provisioning utility to add and delete managed servers cleanly, keeping the domain, AutoConfig, and the database registration in sync. Stop the affected services first, then delete each phantom server:
# Stop the managed servers / services before topology changes
$ADMIN_SCRIPTS_HOME/adstpall.sh apps/<apps_pwd>
# Delete a phantom managed server (repeat per orphaned server / service type)
perl $AD_TOP/patch/115/bin/adProvisionEBS.pl \
ebs-delete-managedserver \
-contextfile=$CONTEXT_FILE \
-managedsrvname=oacore_server4 \
-servicetype=oacore \
-promptmsg=hide
After removing every phantom server, run AutoConfig on each app node so the context, the domain, and fnd_nodes agree:
$ADMIN_SCRIPTS_HOME/adautocfg.sh
Then confirm fnd_nodes only lists the three real clone nodes, and the Admin Console only shows the expected per-node managed servers. If you find stale node rows after the clean-up, the standard FND_CONC_CLONE.SETUP_CLEAN → AutoConfig sequence on each tier is the canonical way to rebuild the node registration. Run it as APPS, then re-run AutoConfig on every tier:
EXEC FND_CONC_CLONE.SETUP_CLEAN;
COMMIT;
(Always take that step with the DBA team’s sign-off on a shared environment.)
Root cause 2 — The missing DNS A-record
With the phantom servers gone, the redirect still misbehaved intermittently. The reason was simpler and entirely outside EBS: the clone’s web entry hostname had no DNS A-record.
The login flow builds its target URL from the AutoConfig web entry variables. Check them:
It’s worth proving the generated login URL, not just the host variable — if AutoConfig hasn’t fully taken, these still show a PROD host and you’ll be chasing DNS for a problem that lives in the context:
grep -i "webentry" $CONTEXT_FILE
grep -i "login_page" $CONTEXT_FILE
The context correctly named clone-apps.example.com as s_webentryhost. But on the app nodes — and for clients — that name did not resolve:
nslookup clone-apps.example.com
# ** server can't find clone-apps.example.com: NXDOMAIN
dig +short clone-apps.example.com
# (empty)
Because the clone web entry host was not resolvable, requests that relied on generated URLs could not consistently resolve back to the clone environment. At the same time, stale PROD references still lived inside the cloned WebLogic topology. Together these two conditions caused redirects to be generated using PROD host information, sending users away from the clone — which is why fixing the phantom servers alone wasn’t enough. To be clear, EBS does not contain any built-in mechanism that redirects a clone to production. Such redirects are almost always caused by stale configuration, profile options, WebLogic topology artifacts, load balancer settings, or DNS resolution — not by EBS doing anything magical. The browser is simply following a redirect built from stale host values. Name resolution has to work and the topology has to be clean.
Fixing it
Add an A-record for the clone web entry host in the appropriate DNS zone, pointing at the clone’s web-tier listen address (or its load balancer / OCI public-or-private IP, depending on your access path):
clone-apps.example.com. IN A 10.x.x.x
If a DNS change isn’t immediately possible and you only need the app nodes to resolve it for validation, a temporary /etc/hosts entry on each of appnode1/2/3 will confirm the theory — but a proper A-record is the real fix, because clients need to resolve it too:
Before concluding the issue persists, test using an incognito/private browser session or clear the browser cache. Browsers frequently cache redirects, cookies, and DNS information that can make a corrected environment appear unchanged.
Root cause 3 — Stale profile option URLs (the other usual suspect)
Even when DNS resolves and the WebLogic topology is clean, a clone can still throw users at PROD because profile option values were copied straight from the source. These are among the most common causes of a redirect-to-PROD, and they deserve a deliberate check rather than a passing glance. The usual culprits are APPS_WEB_AGENT, APPS_FRAMEWORK_AGENT, APPS_SERVLET_AGENT, and ICX_FORMS_LAUNCHER.
Target them directly:
COL profile_option_name FORMAT a30
COL profile_option_value FORMAT a80
SELECT fpo.profile_option_name,
fpov.profile_option_value
FROM fnd_profile_option_values fpov,
fnd_profile_options_vl fpo
WHERE fpov.profile_option_id = fpo.profile_option_id
AND fpo.profile_option_name IN
('APPS_WEB_AGENT',
'APPS_FRAMEWORK_AGENT',
'APPS_SERVLET_AGENT',
'ICX_FORMS_LAUNCHER');
Or sweep more broadly for any value still carrying a PROD host:
COL profile_option_name FORMAT a30
COL profile_option_value FORMAT a80
SELECT fpo.profile_option_name,
fpov.profile_option_value
FROM fnd_profile_option_values fpov,
fnd_profile_options_vl fpo
WHERE fpov.profile_option_id = fpo.profile_option_id
AND UPPER(fpov.profile_option_value) LIKE '%PROD%';
Most of these are AutoConfig-managed, so the right fix is almost always to correct the context and re-run AutoConfig rather than to update the profile value by hand. Hand-updating a profile that AutoConfig owns just means it reverts on the next run. If AutoConfig keeps recreating the wrong value, fix the context file or web entry settings first; otherwise the next AutoConfig run simply reintroduces the problem.
Other places a PROD reference hides: OHS and the OCI load balancer
A surprising share of clone redirects originate outside the database and the WebLogic domain entirely — in the web tier configuration that AutoConfig generates, or in the load balancer sitting in front of it. Worth checking these early rather than last.
Oracle HTTP Server (OHS). Generated OHS config — mod_wl_ohs.conf chief among them — can carry PROD host references. Rather than hard-coding version-specific paths, grep the web-tier and FMW homes broadly:
grep -R "prod-apps.example.com" \
$INST_TOP \
$EBS_DOMAIN_HOME/config \
$FMW_HOME \
$ORACLE_HOME 2>/dev/null
OCI load balancer / reverse proxy. If an OCI Load Balancer (or any reverse proxy) fronts the environment, the redirect can be introduced at that layer even after EBS is fully corrected. Verify:
Host header preservation — the backend should receive the clone host, not a rewritten PROD one.
Backend set configuration — backends point at the clone app nodes, not PROD.
SSL/TLS termination — the protocol and host the LB forwards match what AutoConfig expects (s_webentryurlprotocol, s_active_webport).
Redirect rules / rule sets — no listener rule is rewriting the host to PROD.
I’ve seen an OCI LB listener keep sending users to PROD long after the EBS tier itself was spotless, purely because of a stale redirect rule on the listener.
Custom code and JARs. Customizations are a notorious hiding place — hardcoded URLs in custom packages, JSPs, or Java survive every clone untouched by AutoConfig. Grep the custom homes too:
grep -R "prod-apps.example.com" \
$XX_TOP \
$JAVA_TOP \
$CUSTOM_TOP 2>/dev/null
Validation checklist after remediation
1.nslookup / dig resolves clone-apps.example.com from all three app nodes and from a client workstation.
2. WebLogic Admin Console lists only the expected per-node managed servers, all in RUNNING state.
3.fnd_nodes contains only appnode1/2/3 (check support_cp, support_web, support_admin); no PROD host references remain.
4. No stale source context files are still registered. Old registrations sometimes survive a clone and cause confusion later during AutoConfig or service management:
COL node_name FORMAT a20
COL ctx_file FORMAT a60
SELECT node_name,
ctx_type,
NVL(path,'-') AS ctx_file,
status
FROM fnd_oam_context_files
WHERE status = 'S'
ORDER BY node_name;
In a clean clone, every active context file should belong to the clone environment. Any remaining source-environment context registration should be reviewed and removed before further AutoConfig runs.
5. The profile options from Root cause 3 (APPS_WEB_AGENT, APPS_FRAMEWORK_AGENT, APPS_SERVLET_AGENT, ICX_FORMS_LAUNCHER) all carry clone values, not PROD — re-run the targeted query from that section to confirm.
6. The login page loads from the clone URL and — critically — the post-login redirect stays onclone-apps.example.com, never bouncing to prod-apps.example.com.
7. Sweep the entire configuration for the PROD hostname, not just the context file. PROD remnants love to hide in OHS configs, mod_wl_ohs.conf, generated XML, and custom integrations:
grep -R "prod-apps.example.com" \
$INST_TOP \
$EBS_DOMAIN_HOME/config \
$FMW_HOME 2>/dev/null
Anything this turns up needs to be corrected (and usually re-generated via AutoConfig) before users find it for you.
8. No context file registered in the database still points at a PROD path. Stale PROD context registrations can cause odd behaviour long after the clone:
COL node_name FORMAT a20
COL path FORMAT a70
SELECT node_name,
path
FROM fnd_oam_context_files
WHERE UPPER(path) LIKE '%PROD%';
9. If an OCI Load Balancer or reverse proxy fronts the environment, verify its listener rules, backend sets, host-header forwarding, and SSL termination settings contain no PROD references.
Lessons learned / a small post-clone checklist
Cloning R12.2 onto a shared file system multi-node tier adds two checks that single-node clones let you skip:
Audit the managed-server topology immediately after adcfgclone. On a shared file system the source’s managed servers ride along inside config.xml. Delete phantoms with adProvisionEBS.pl ebs-delete-managedserver, never by editing XML, and re-run AutoConfig.
Resolve the web entry hostname before you trust the login page. A clone that “logs you into PROD” is very often a DNS problem wearing an EBS costume. Create the A-record as part of the clone runbook, not as a reaction to the redirect.
Treat a redirect-to-PROD as a stop-the-line event. Until both topology and name resolution are clean, assume the clone can still touch production and keep testers out.
Run FND_CONC_CLONE.SETUP_CLEAN before AutoConfig in cloned environments. This rebuilds node registrations and context metadata cleanly and helps prevent stale topology information from the source environment persisting in the clone. (On a shared environment, run it with the DBA team’s sign-off.)
In this case, three unglamorous root causes — leftover managed servers, a missing DNS record, and stale profile option URLs — combined to produce the redirect. In practice, however, most redirect-to-PROD incidents come down to one broader problem: stale production references surviving the clone process. The fastest path to resolution is a systematic sweep of WebLogic, DNS, profile options, context files, OHS configuration, load balancers, and custom code until every production reference is gone.
Have you hit a different flavour of the clone redirect? The web entry variables, SSL/load-balancer termination, and s_login_page overrides each have their own way of sending you to the wrong host — happy to compare notes in the comments.
Many Oracle EBS DBAs know how to run ADOP. Fewer understand why the dual file system exists or what is actually happening behind each phase. This post fills that gap — the architecture first, then the lifecycle, then the real-world traps.
Why two file systems exist
The biggest shift moving from R12.1 to R12.2 is that the application tier is no longer one file system. R12.2 keeps two full, identical copies of the application code — fs1 and fs2 — plus a third, non-editioned area, fs_ne.
At any moment one of fs1/fs2 is the run file system (what users are connected to) and the other is the patch file system (where ADOP applies patches in the background). That is the whole point: you patch the idle copy while users keep working on the live one, then switch over in a short cutover window.
The layout on disk
/u01/oracle/<SID>/
│
├── fs1 (RUN or PATCH)
├── fs2 (PATCH or RUN)
└── fs_ne (Non-editioned — never swaps)
The two never have fixed roles. After every cycle the labels swap, so fs1 might be run today and patch after the next cutover:
Before Cutover After Cutover
Users Users
| |
v v
RUN = fs1 RUN = fs2
PATCH = fs2 PATCH = fs1
If you remember only one thing from this article, remember this: fs1 and fs2 are just labels. RUN and PATCH are the roles that matter.
Never hardcode a path assuming fs1 is always live. Read the environment, don’t memorize it.
Determining the current run and patch file systems
This is one of the most common questions in R12.2, and the answer is in the environment, not in your head:
RUN_BASE = /u01/oracle/<SID>/fs1
PATCH_BASE = /u01/oracle/<SID>/fs2
FILE_EDITION = run
FILE_EDITION tells you which copy your current shell is pointed at. A surprising number of “my patch went to the wrong place” problems are just someone working in the wrong edition’s shell.
What fs_ne is and why it exists
Most write-ups mention fs_ne and move on. It matters because it holds everything that must survive a cutover unchanged:
fs_ne (non-editioned) contains data that must NOT switch at cutover:
- Concurrent Manager logs and output
- Application / debug logs
- Inbound interface files
- Outbound interface files
- Custom file drops
- Temp files
If these lived inside fs1 and fs2, they would belong to whichever copy was live — and the moment you cut over to the other copy, those files would appear to “vanish.” Pulling them into a single non-editioned area means logs and interface files stay put no matter which file system is running.
File system editions and database editions
R12.2 pairs the dual file system with Edition-Based Redefinition (EBR) on the database. Each patching cycle stacks a new database edition on top of the last:
COL edition_name FORMAT A28
COL parent_edition_name FORMAT A28
COL usable FORMAT A8
SELECT edition_name,
NVL(parent_edition_name,'-') AS parent_edition_name,
usable
FROM dba_editions
ORDER BY edition_name;
The run edition is the currently active database edition. During prepare, ADOP creates a new child patch edition where database changes are applied. During cutover, that patch edition becomes the new run edition. The file system and the database edition move together — patch edition on the patch file system, run edition on the run file system.
The ADOP cycle, phase by phase
ADOP (AD Online Patching) drives everything. A full cycle is five phases:
prepare — validates the environment, synchronizes the patch file system with the run file system when necessary, and creates the new database patch edition. Run and patch are now identical and ready to diverge.
apply — applies your patch(es) to the patch file system and patch edition only. Users are untouched. You can apply many patches across multiple apply calls in one cycle.
finalize — does the heavy compile/precompute work it can do before cutover, to keep the downtime window short.
cutover — the only disruptive step. Services bounce, the patch file system becomes the new run, and the patch edition becomes the new run edition.
cleanup — drops obsolete editions and old objects so the system is ready for the next cycle.
“Online patching” — so why do users still get disconnected?
This is the question every customer asks. The honest answer: only cutover is disruptive.
Prepare -> No downtime
Apply -> No downtime
Finalize -> No downtime
Cutover -> DOWNTIME (services stop and restart on the patched FS)
Cleanup -> No downtime
All the slow work — applying patches, compiling, copying files — happens on the idle patch file system while users keep working. The only outage is the cutover window.
What actually happens during cutover
During cutover:
1. Application services stop.
2. Database patch edition becomes the new run edition.
3. Patch file system becomes the new run file system.
4. Context files are updated.
5. Application services restart.
6. Users reconnect to the newly patched environment.
That sequence is the bridge between the architecture and what you actually watch happen in the logs during your maintenance window.
Monitoring an ADOP session
Check where any cycle stands with the status command and the AD tables:
adop -status
COL node_name FORMAT A14
COL prepare FORMAT A8
COL apply FORMAT A8
COL cutover FORMAT A8
COL cleanup FORMAT A8
COL status FORMAT A12
SELECT adop_session_id,
node_name,
NVL(prepare_status,'-') AS prepare,
NVL(apply_status,'-') AS apply,
NVL(cutover_status,'-') AS cutover,
NVL(cleanup_status,'-') AS cleanup,
NVL(status,'-') AS status
FROM ad_adop_sessions
ORDER BY adop_session_id DESC;
A clean idle system shows the last session fully completed. A row stuck mid-phase is an abandoned cycle that must be sorted before the next patch — you cannot start a fresh prepare cleanly on top of a half-finished one.
fs_clone: the part that bites people
fs_clone resyncs the patch file system from run so the two start identical:
adop phase=fs_clone
You run it explicitly when the file systems have drifted — after a failed cutover, a manual change to one side, or a long gap since the last cycle. prepare also triggers it internally when it detects drift.
It rarely fails because of the clone logic. It fails on file ownership and permissions. fs_clone deletes and rebuilds the patch file system’s middleware home (FMW_Home), and if something there is not owned by the apps OS user, the delete stalls. The classic case is OHS files that ended up root-owned after a privileged process touched them. The clone tries to remove FMW_Home, hits files it cannot delete, and aborts.
Safe recovery is to move the blocking tree aside rather than force-delete it:
# As the apps owner, after confirming what is in there:
mv $PATCH_BASE/FMW_Home $PATCH_BASE/FMW_Home_stale_$(date +%Y%m%d)
# then re-run
adop phase=fs_clone
mv over rm -rf is deliberate. A recursive force-delete on a partially root-owned tree on a production node is how a recoverable patch problem becomes a restore-from-backup problem. Rename it aside, let fs_clone rebuild cleanly, and clear the stale copy later once the system is confirmed healthy. If root-owned files genuinely must be removed, get the ownership corrected through the proper channel — not a blind sudo rm on a live box.
Plan for space
Remember that R12.2 maintains two complete application file systems plus fs_ne. Any major patching activity, an fs_clone, or a technology stack upgrade can temporarily require significantly more space than a comparable R12.1 environment. fs_clone failing with cryptic errors is often nothing more than a full mount — check headroom before you start, not after it fails halfway. This is a lesson many administrators learn the hard way.
ADOP command quick reference
adop -status # where does the current/last cycle stand
adop phase=prepare # sync patch FS, create patch edition
adop phase=apply patches=12345678 # apply patch to patch FS / patch edition
adop phase=finalize # pre-cutover compile work
adop phase=cutover # the downtime step — switch run/patch
adop phase=cleanup # drop obsolete editions and objects
adop phase=fs_clone # resync patch FS from run after drift
adop phase=abort # cleanly abandon an in-progress cycle
Common real-world mistakes
1. Assuming fs1 is always RUN.
2. Applying custom changes directly on the RUN file system.
3. Forgetting fs_clone after manual changes to one file system.
4. Running out of disk space during fs_clone or apply.
5. Leaving an ADOP session unfinished (blocks the next prepare).
6. Ignoring cleanup for months (editions pile up, performance degrades).
7. Modifying or letting processes touch FMW_Home as root.
8. Making custom changes on the RUN file system and forgetting to sync them
to PATCH before the next ADOP cycle (lost custom JSPs, forms, and reports
after cutover; unexplained differences between fs1 and fs2).
Most production ADOP pain traces back to one of these eight, not to the tool itself.
In summary
Oracle EBS R12.2 achieves near-zero downtime patching by combining two application file systems (fs1/fs2) with Edition-Based Redefinition in the database. While users continue working on the run edition, patches are applied to an isolated patch edition. During cutover, Oracle switches application services from the old run file system and database edition to the newly patched run file system and edition.
A few principles to carry away:
Run/patch roles are not fixed — read the environment, never assume fs1 is live.
fs_ne is non-editioned so logs and interface files survive cutover instead of vanishing.
Only cutover causes an outage. Everything else runs during business hours.
Most fs_clone failures are ownership problems on FMW_Home. Move the blocking tree aside and let it rebuild; reach for rm -rf only as an absolute last resort and never against a partially root-owned tree.
Treat disk space, edition awareness, and cleanup as routine discipline. The architecture is forgiving when you respect those and unforgiving when you don’t.
Understanding ADOP becomes much easier when you stop thinking about patches and start thinking about roles: one file system serves users, the other is prepared for the next cutover.
Disclaimer: All paths, identifiers, and examples in this post are anonymized and generic. They do not represent any specific client, employer, or environment. Views are my own.
Disclaimer: This article is based on field experience and publicly shareable troubleshooting steps. Oracle MOS notes are referenced for further reading. Customers with active Oracle Support should review the referenced MOS documents for complete guidance.
Category: Oracle EBS R12.2 | Cloning | Troubleshooting Level: Intermediate to Advanced Reference: MOS Doc ID 454427.1
Introduction
Cloning an Oracle E-Business Suite R12.2 application tier is a common activity — whether for setting up a DEV, TEST, or UAT environment. While the process is well-documented, real-world scenarios often throw up issues that aren’t immediately obvious from the error messages alone.
In this post, I walk through two common issues encountered during an EBS R12.2 app tier clone using adcfgclone.pl:
RC-50221: Port Pool Not Free — CloneContext failing due to an already-occupied port pool
FRM-92101: Forms Session Failed During Startup — Forms not launching due to missing library dependencies or invalid symlinks
Both issues are real, well-known, and worth documenting together since they often appear in sequence during a clone.
Environment Details
Oracle EBS Version: R12.2.x
OS: Oracle Linux 7 (OL7) / RHEL 7
Clone type: Application Tier only (adcfgclone.pl appsTier)
When running adcfgclone.pl appsTier, the CloneContext step prompts for a Target System Port Pool and throws the following warning/error:
Checking the port pool 0
RC-50221: Warning: Port Pool 0 is not free.
Please check logfile $COMMON_TOP/clone/bin/CloneContext_<timestamp>.log for conflicts.
RC-00201: Error: Not a valid port pool number
ERROR: Context creation not completed successfully.
CloneContext Log Location
$COMMON_TOP/clone/bin/CloneContext_<timestamp>.log
# Or typically:
/u01/APPLTOP/EBSapps/comn/clone/bin/CloneContext_<timestamp>.log
Root Cause
Port pool 0 is already in use by another EBS instance running on the same server, causing CloneContext validation to fail. Port pool 0 generally corresponds to the initially configured EBS port set for that environment. Actual port assignments vary by release, topology, and previous configuration changes.
Review /tmp/portcheck.txt for any conflicts before selecting a port pool number.
Fix
Target System Port Pool [0-99] : 1
Checking the port pool 1
done: Port Pool 1 is free
Important Note: Using a different port pool causes Oracle EBS to calculate a different set of ports based on the predefined port pool mapping. The resulting ports are not always a simple increment of one and should be verified in the generated context file after CloneContext completes.
Always verify the actual assigned ports: grep -i "port" $CONTEXT_FILE | grep -v "#" cat $INST_TOP/admin/out/portpool.lst
If your firewall rules, load balancer, or application configuration depend on specific port numbers, plan accordingly or stop the existing instance first to free pool 0.
Patch File System Port Pool
For R12.2 dual file system architecture, the Patch file system must use a different port pool than the Run file system. AutoConfig and cloning utilities will calculate a separate set of ports accordingly.
Patch file system should have different port pool than Run file system.
Target System Port Pool [0-99] : 2
Issue 2: FRM-92101 — Forms Session Failed During Startup
Symptom
FRM-92101: There was a failure in the Forms Server during startup.
Please look into the web-server log file for details.
Java Exception:
oracle.forms.net.ConnectionException: Forms session <1> failed during startup:
no response from runtime process
What This Means
The WebLogic Forms server (forms_server1) is running, but the underlying Forms runtime process (frmweb) is failing to start. WebLogic receives no response from it, and the session times out. FRM-92101 can have more than one root cause — the key is to run frmweb directly to identify which one applies.
The output will indicate which root cause applies:
Undefined symbol errors (e.g. Symbol nnftboot was referenced...not found) → Root Cause 1: Invalid ldflags symlink
Missing shared library error (e.g. libXm.so.2: cannot open shared object file) → Root Cause 2: Missing OpenMotif symlink
Root Cause 1: Invalid ldflags Symlink
Symptom
Running frmweb directly produces undefined symbol errors:
rtld: 0712-001 Symbol nnftboot was referenced from module frmweb(), but a runtime definition of the symbol was not found.
rtld: 0712-001 Symbol ldap_search_s was referenced from module frmweb(), but a runtime definition of the symbol was not found.
Root Cause
Investigation revealed that the ldflags symbolic link under $ORACLE_HOME/lib32 was missing or pointing to an incorrect location, causing the Forms executable to fail during startup. This is a known issue after fresh installations or clones and is referenced in MOS Doc ID 454427.1.
Verify ldflags
ls -la $ORACLE_HOME/lib32/ldflags
If the symlink is missing or pointing to a non-existent path, it needs to be corrected to point to $ORACLE_HOME/lib/ldflags.
Fix
After correcting the ldflags symbolic link and relinking the Forms components, frmweb started successfully. The relinking is performed using the Forms makefile under the 10.1.2 Oracle Home. Refer to MOS Doc ID 454427.1 for the complete steps applicable to your environment.
Root Cause 2: Missing OpenMotif Library Symlink
Symptom
Running frmweb directly produces a missing shared library error:
error while loading shared libraries:
libXm.so.2: cannot open shared object file: No such file or directory
Root Cause
The Oracle Forms runtime binary frmweb is compiled against libXm.so.2 (OpenMotif 2.x). On newer Linux versions (OL7/OL8/RHEL7/RHEL8), the installed OpenMotif package provides libXm.so.4, but the compatibility symlink libXm.so.2 → libXm.so.4 is missing.
Verify with ldd
ldd $ORACLE_HOME/bin/frmweb | grep Xm
# Before fix:
libXm.so.2 => not found
# After fix:
libXm.so.2 => /usr/lib/libXm.so.4 (0x...)
Option A: Create the Missing Symlink (Quick Fix)
📌 Note on 32-bit vs 64-bit Libraries: On some Linux platforms the required library may reside under /usr/lib64 rather than /usr/lib. Always verify the actual location before creating symbolic links.
# Step 1: Find the actual location of libXm.so.4
ls -l /usr/lib/libXm.so*
ls -l /usr/lib64/libXm.so*
# Step 2: Create the symlink (run as root)
# If library is in /usr/lib:
ln -s /usr/lib/libXm.so.4 /usr/lib/libXm.so.2
# If library is in /usr/lib64:
ln -s /usr/lib64/libXm.so.4 /usr/lib64/libXm.so.2
# Step 3: Verify
ldd $ORACLE_HOME/bin/frmweb | grep Xm
# Expected: libXm.so.2 => /usr/lib/libXm.so.4 (or /usr/lib64/...)
cd $ADMIN_SCRIPTS_HOME
./adstpall.sh apps/<apps_password>
./adstrtal.sh apps/<apps_password>
# Or start Forms server specifically:
./admanagedsrvctl.sh start forms_server1
After starting the services, verify that frmweb runtime processes are spawning successfully:
ps -ef | grep frmweb
ps -ef | grep forms
Then launch a Forms-based responsibility or function to confirm successful startup.
Conclusion
Both RC-50221 and FRM-92101 are common issues that can delay Oracle EBS R12.2 cloning activities. While the error messages may initially appear unrelated, a structured troubleshooting approach focusing on log analysis, port validation, and runtime dependency checks can quickly identify the root cause and reduce downtime.
Summary
Issue
Root Cause
Fix
RC-50221: Port Pool Not Free
Another EBS instance occupying port pool 0
Use port pool 1 (or stop existing instance first)
FRM-92101: ldflags issue
ldflags symlink missing or pointing to wrong location
Fix ldflags symlink and relink Forms components (MOS Doc ID 454427.1)
FRM-92101: OpenMotif issue
libXm.so.2 symlink missing after clone
ln -s /usr/lib[64]/libXm.so.4 /usr/lib[64]/libXm.so.2 or install openmotif21
Key Diagnostic Commands Cheat Sheet
# Check which port pools are in use
grep -i "port_pool" $CONTEXT_FILE
cat $INST_TOP/admin/out/portpool.lst
# Validate port availability
perl $AD_TOP/bin/txkValidateRollupPorts.pl -contextfile=$CONTEXT_FILE -outfile=/tmp/portcheck.txt
# Check running WebLogic processes
ps -ef | grep weblogic | grep -v grep
# Run frmweb manually to see actual error
$ORACLE_HOME/bin/frmweb
# Check frmweb library dependencies
ldd $ORACLE_HOME/bin/frmweb | grep Xm
# Check ldflags symlink
ls -la $ORACLE_HOME/lib32/ldflags
# Check OpenMotif installation
rpm -qa | grep motif
# Check Forms runtime processes
ps -ef | grep frmweb
# Check Forms server log
tail -100 $EBS_DOMAIN_HOME/servers/forms_server1/logs/forms_server1.log
Lessons Learned
FRM-92101 is often a symptom, not the root cause. The WebLogic log may show the Java exception, but the actual failure is in the native frmweb binary. Always run frmweb directly to see the real error.
FRM-92101 has multiple root causes. Check the actual error output from frmweb to distinguish between an ldflags relinking issue and a missing OpenMotif library symlink.
Use ldd to diagnose missing shared library dependencies. It quickly identifies unresolved libraries without having to dig through multiple log files.
Verify port pool availability before starting a clone. Stop existing instances or choose a different pool to avoid RC-50221 errors.
Always validate the resulting ports in the generated context file after CloneContext completes. Port pool mapping is predefined and the assigned ports should be confirmed before proceeding.
Newer Linux builds may include libXm.so.4 but not the compatibility symlink required by Oracle Forms. OL7/OL8 installations do not always create libXm.so.2 automatically.
Document port assignments after every clone to avoid firewall and load balancer configuration issues down the line.
Keywords
Oracle EBS R12.2 Clone
Oracle EBS R12.2 Forms Error
RC-50221 Port Pool Not Free
RC-50221 CloneContext Failed
FRM-92101 Forms Session Failed During Startup
FRM-92101 No Response From Runtime Process
ldflags symlink Oracle Forms
libXm.so.2 not found
Oracle Forms Runtime Process
adcfgclone.pl appsTier
adcfgclone.pl appsTier Error
Oracle EBS Clone Troubleshooting
Oracle Linux 7 OpenMotif Issue
OpenMotif
frmweb
CloneContext
References
MOS Doc ID 454427.1 — FRM-92101: There was a failure in the Forms Server during startup
This post documents a real production incident where an Oracle E-Business Suite (EBS) environment became completely unresponsive due to a full Fast Recovery Area (FRA). Users were reporting that everything was either slow or hanging — a classic sign that something fundamental had broken at the database layer.
Symptoms
EBS application users reporting sessions hanging or extremely slow response
sqlplus apps/<password> failing with:
ORA-00257: Archiver error. Connect AS SYSDBA only until resolved.
Non-SYSDBA connections completely blocked
30 FNDLIBR (Concurrent Manager) processes running on the application server — higher than expected for a QA environment
Environment
Oracle Database 19c (CDB/PDB architecture)
PDB: Application database PDB
EBS 12.2 on AIX
FRA configured on ASM diskgroup (+RECO), size 4095 GB
No explicit log_archive_dest — archivelogs defaulting to FRA
Step 1 — Identify the Root Cause
Connected to the CDB as SYSDBA and confirmed the error:
SHOW PARAMETER db_recovery_file_dest;
NAME TYPE VALUE
--------------------------- ----------- ------
db_recovery_file_dest string +RECO
db_recovery_file_dest_size big integer 4095G
Checked FRA usage:
COLUMN name FORMAT A10
COLUMN limit_gb FORMAT 999,999.99 HEADING 'LIMIT GB'
COLUMN used_gb FORMAT 999,999.99 HEADING 'USED GB'
COLUMN reclaimable_gb FORMAT 999,999.99 HEADING 'RECLAIMABLE GB'
COLUMN number_of_files FORMAT 99999 HEADING 'FILES'
SELECT name,
ROUND(space_limit/1024/1024/1024,2) limit_gb,
ROUND(space_used/1024/1024/1024,2) used_gb,
ROUND(space_reclaimable/1024/1024/1024,2) reclaimable_gb,
number_of_files
FROM v$recovery_file_dest;
Output:
NAME LIMIT GB USED GB RECLAIMABLE GB FILES
---------- ----------- ----------- -------------- ------
+RECO 4,095.00 4,075.65 .00 2426
FRA was at 99.5% — 4,075 GB used out of 4,095 GB, zero reclaimable, 2,426 archivelog files.
This was the root cause. With the FRA full and nothing reclaimable, the archiver (ARCn) process could not write new archive logs, blocking all database activity.
Step 2 — Assess Backup Status Before Taking Action
Before deleting any archivelogs, it is critical to understand the backup posture. Blindly deleting archivelogs without knowing the backup state can leave the database unrecoverable.
Started an RMAN session and ran a crosscheck first:
rman target /
RMAN> crosscheck archivelog all;
All 2,416 objects returned “validation succeeded” — no expired logs. This meant the FRA was genuinely full of valid, undeleted archivelogs.
Next, checked backup history:
RMAN> list backup summary;
Backups were listed daily going back several weeks — however, on closer inspection:
SELECT session_key,
TO_CHAR(start_time,'DD-MON-YY HH24:MI') start_time,
TO_CHAR(end_time,'DD-MON-YY HH24:MI') end_time,
status,
input_bytes,
output_bytes
FROM v$rman_backup_job_details
ORDER BY start_time DESC
FETCH FIRST 10 ROWS ONLY;
INPUT_BYTES = 0 — no datafiles were being backed up
OUTPUT_BYTES = 98304 (96 KB) — only a controlfile/SPFILE autobackup
Jobs completing in under a minute — impossible for a real full DB backup
Additionally:
RMAN> list backup of archivelog all;
-- specification does not match any backup in the repository
No archivelog backups existed at all. The scheduled backup job was not performing proper datafile or archivelog backups — a separate issue to be addressed post-incident.
Step 3 — Escalation and Approval
Given that there were no archivelog backups and the datafile backup was questionable, the decision to delete archivelogs and Approval was obtained with the instruction to start conservatively — delete older than 15 days first, then 7 days if needed.
Step 4 — Resolution
First attempt — delete older than 15 days:
RMAN> delete noprompt archivelog until time 'SYSDATE-15';
RMAN returned warnings:
RMAN-08138: warning: archived log not deleted - must create more backups
This is because the RMAN retention policy was blocking deletion of logs that had never been backed up. With Tier 3 approval, used the force option to override:
RMAN> delete noprompt force archivelog until time 'SYSDATE-15';
This ran successfully, deleting archivelogs from January through late April.
Over 3,800 GB freed — FRA dropped from 99.5% to ~6%.
Step 5 — Verification
Forced a log switch and verified archiver status:
ALTER SYSTEM ARCHIVE LOG CURRENT;
SELECT dest_id, status, error
FROM v$archive_dest
WHERE status != 'INACTIVE';
Output:
DEST_ID STATUS ERROR
------- -------- -----
1 VALID
Archiver resumed successfully. Application connectivity was restored and users confirmed sessions were working normally.
Redo Log Status Check
As part of the verification, also confirmed redo log health:
SELECT thread#,
sequence#,
TO_CHAR(first_time,'DD-MON-YY HH24:MI:SS') first_time,
first_change#,
archived,
status
FROM v$log
ORDER BY thread#, sequence#;
THREAD# SEQUENCE# FIRST_TIME FIRST_CHANGE# ARC STATUS
------- --------- ------------------------ ------------- --- -------
1 2417 05-MAY-26 01:56:44 1.7867E+10 NO INACTIVE
1 2418 05-MAY-26 03:27:20 1.7867E+10 NO INACTIVE
1 2419 05-MAY-26 07:57:10 1.7868E+10 NO CURRENT
No stuck or unarchived redo logs — database in healthy state.
Incident Summary
Item
Detail
Error
ORA-00257: Archiver error
Root Cause
FRA (+RECO ASM diskgroup) at 99.5% capacity
FRA Before
4,075 GB used / 2,426 files
FRA After
246 GB used / 149 files
Action Taken
delete noprompt force archivelog until time 'SYSDATE-15'
Space Freed
~3,829 GB
Resolution Time
~45 minutes from identification to restoration
Key Lessons Learned
1. Monitor FRA Proactively
Set up OEM 13c threshold alerts on FRA usage at 70% and 85%. Do not wait for ORA-00257 to discover the problem.
SELECT ROUND(space_used/space_limit*100,2) pct_used
FROM v$recovery_file_dest;
Alert when this exceeds 80%.
2. Always Check Backup Status Before Deleting Archivelogs
In this incident, the backup job appeared to be running daily but was only backing up the controlfile (INPUT_BYTES=0). This is a serious gap — verify actual backup content, not just job status.
3. Investigate the Backup Job
A proper RMAN backup script for a 19c CDB should include:
BACKUP DATABASE PLUS ARCHIVELOG DELETE INPUT;
4. Consider an Archivelog Deletion Policy
If archivelog backups are not being taken, configure an RMAN retention policy to prevent FRA accumulation:
CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 7 DAYS;
5. FRA Sizing Review
Even at 4095 GB, the FRA filled up due to months of uncleaned archivelogs. Review archivelog generation rate and size FRA accordingly, or implement regular cleanup.
Conclusion
ORA-00257 is one of those errors that brings an entire EBS environment to its knees instantly. The fix itself is straightforward — free up FRA space — but the investigation matters. Rushing to delete archivelogs without understanding the backup posture can result in an unrecoverable database.
In this case, careful investigation revealed a deeper issue with the backup job that would have gone unnoticed had we not looked. Always verify, always escalate, and always get approval before deleting recovery-critical files.
Syed Anwar Ahmed is an Oracle Apps DBA with over 11 years of production experience across Oracle EBS, Database, RAC, GoldenGate, and OEM environments. He writes about real-world Oracle incidents at syedanwarahmedoracle.blog.
In this article, we are going to demonstrate the steps to apply the Oracle April 2026 Critical Patch Update (CPU) on a Single Instance Oracle 19c Container Database (CDB). Here we will apply combo patch 39062931 which contains 39034528 (Database Apr 2026 Release Update 19.31.0.0.260421) and 38906621 (OJVM Component Release Update 19.31.0.0.260421). We also cover an important real-world scenario where a previously applied overlay patch 34672698 (ORA-00800 fix) conflicted with the Apr 2026 RU — confirming that the fix is now natively included in the RU itself.
Official References: This walkthrough is based on MOS Note CPU58 — Critical Patch Update (CPU) Apr 2026 for Oracle Database Products — and the Oracle April 2026 CPU Advisory (481 new security patches across all Oracle product families, released 21-Apr-2026).
Patch Availability Note: The Linux x86-64 DB RU 39034528 was delayed post the standard Apr 21 release. Per MOS Note CPU58, the Linux x86-64 ETA was 15-May-2026. Always check CPU58 for the latest availability before scheduling your patching window.
Downtime Notice: Applying a DB Release Update on a Single Instance (non-RAC) database requires a full database shutdown. Plan for a maintenance window accordingly and notify application teams in advance.
Terminology Note: Oracle’s quarterly security bundle is commonly referred to as the Critical Patch Update (CPU). However, Oracle now delivers database fixes as Release Updates (RU). The term CPU refers to the overall security advisory, while RU is the actual patch artifact you apply. Both terms are used interchangeably in the field — this post uses CPU to align with the official advisory title.
Environment Details
Parameter
Value
Database Type
Single Instance Oracle 19c CDB
OS
Oracle Linux x86-64
Starting Version
19.30.0.0.0 (Jan 2026 RU)
Target Version
19.31.0.0.0 (Apr 2026 RU)
Combo Patch
39062931
DB RU Patch
39034528
OJVM RU Patch
38906621
Overlay Patch (conflict)
34672698 (ORA-00800 fix — rolled back, natively included in 19.31)
OPatch Version
12.2.0.1.51
ORACLE_HOME
/u03/app/oracle/product/19.0.0.0/db_1
Section 1: Pre-Patch Checks
1.1 Verify current database version
sqlplus / as sysdba
SQL> select version_full from v$instance;
VERSION_FULL
-----------------
19.30.0.0.0
1.2 Check existing SQL patch history (dba_registry_sqlpatch)
SQL> set lines 200
SQL> col description format a50
SQL> col status format a10
SQL> col action_time format a30
SQL> select patch_id, action, status, action_time, description
2 from dba_registry_sqlpatch
3 order by action_time;
PATCH_ID
ACTION
STATUS
ACTION_TIME
DESCRIPTION
29517242
APPLY
SUCCESS
2022-10-18 02:14:37
Database Release Update 19.17
38632161
APPLY
SUCCESS
2026-01-21 04:45:06
Database Release Update 19.30.0.0.260120
38523609
APPLY
SUCCESS
2026-01-21 04:49:43
OJVM Release Update 19.30.0.0.260120
1.3 Check component registry (dba_registry)
SQL> col comp_name format a45
SQL> select comp_id, comp_name, version, status from dba_registry order by comp_id;
COMP_ID
COMP_NAME
VERSION
STATUS
APS
OLAP Analytic Workspace
19.0.0.0.0
VALID
CATALOG
Oracle Database Catalog Views
19.0.0.0.0
VALID
CATJAVA
Oracle Database Java Packages
19.0.0.0.0
VALID
CATPROC
Oracle Database Packages and Types
19.0.0.0.0
VALID
CONTEXT
Oracle Text
19.0.0.0.0
VALID
DV
Oracle Database Vault
19.0.0.0.0
VALID
JAVAVM
JServer JAVA Virtual Machine
19.0.0.0.0
VALID
OLS
Oracle Label Security
19.0.0.0.0
VALID
ORDIM
Oracle Multimedia
19.0.0.0.0
VALID
OWM
Oracle Workspace Manager
19.0.0.0.0
VALID
SDO
Spatial
19.0.0.0.0
VALID
XDB
Oracle XML Database
19.0.0.0.0
VALID
XML
Oracle XDK
19.0.0.0.0
VALID
XOQ
Oracle OLAP API
19.0.0.0.0
VALID
1.4 Check invalid objects (baseline)
SQL> select count(*) from dba_objects where status = 'INVALID';
COUNT(*)
----------
8
Note: Record the baseline invalid object count. Any new invalids introduced by patching should be investigated. After datapatch, Oracle-supplied objects marked temporarily INVALID will recompile automatically. Only non-Oracle invalids or objects still INVALID after 24 hours need attention.
Note on OCW patch 29585399: This OCW (Oracle Clusterware) patch appears in the OPatch inventory even on Single Instance (non-RAC) environments. This is expected behaviour — it is included in the base Oracle Home installation and is not a sign of misconfiguration.
Section 2: Upgrade OPatch
Before applying any patch, always upgrade OPatch to the latest version. Download the latest OPatch (p6880880) from MOS and apply.
# As oracle user
cd $ORACLE_HOME
mv OPatch OPatch_backup_$(date +%Y%m%d)
unzip /<patch_staging_dir>/p6880880_190000_Linux-x86-64.zip -d $ORACLE_HOME
# Verify new version
$ORACLE_HOME/OPatch/opatch version
# Expected: OPatch Version: 12.2.0.1.51
Section 3: Stage the Patches
# Create staging directory
mkdir -p /<patch_staging_dir>/APR_2026
# Unzip combo patch (contains both DB RU and OJVM RU)
cd /<patch_staging_dir>/APR_2026
unzip p39062931_190000_Linux-x86-64.zip
# Verify extraction
ls -la
# Should show: 38906621/ 39034528/
Section 4: Conflict Check
# Check DB RU for conflicts
cd /<patch_staging_dir>/APR_2026/39034528
$ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./
# Check OJVM RU for conflicts
cd /<patch_staging_dir>/APR_2026/38906621
$ORACLE_HOME/OPatch/opatch prereq CheckConflictAgainstOHWithDetail -ph ./
Real-World Conflict Scenario (ZOP-47): In this environment, the conflict check returned a conflict between overlay patch 34672698 and the new DB RU 39034528. This is because the ORA-00800 fix originally delivered by 34672698 is now natively included in the 19.31 RU. The resolution is to roll back the overlay patch first before applying the RU. This is a standard ZOP-47 scenario — the overlay is superseded, not incompatible.
Section 5: System Space Check
# Check available space before patching
$ORACLE_HOME/OPatch/opatch prereq CheckSystemSpace -ph /<patch_staging_dir>/APR_2026/39034528
$ORACLE_HOME/OPatch/opatch prereq CheckSystemSpace -ph /<patch_staging_dir>/APR_2026/38906621
# Also verify OS-level space
df -h $ORACLE_HOME
df -h /tmp
Section 6: Take Oracle Home TAR Backup
# As oracle user — use relative path approach to avoid absolute path issues
cd $(dirname $ORACLE_HOME)
nohup tar -czf /<backup_dir>/ORACLE_HOME_backup_$(date +%Y%m%d).tar.gz \
$(basename $ORACLE_HOME) > /tmp/oh_tar_backup.log 2>&1 &
echo "Backup PID: $!"
tail -f /tmp/oh_tar_backup.log
Note: Using $(dirname $ORACLE_HOME) and $(basename $ORACLE_HOME) avoids embedding absolute paths in the tar archive, making restoration cleaner. The nohup & pattern allows the backup to run in background without being killed if the session disconnects.
Section 7: RMAN Backup (Verify)
# Verify a recent RMAN backup exists before proceeding
rman target /
RMAN> list backup summary;
RMAN> exit;
Important: Never apply a patch without confirming a valid RMAN backup. If the latest backup is stale, take a fresh full backup before proceeding. The Oracle Home TAR covers binaries — RMAN covers your data.
Because patch 34672698 conflicts with the Apr 2026 RU (its fix is now natively included), we must roll it back first.
export PATH=$ORACLE_HOME/OPatch:$PATH
cd $ORACLE_HOME/OPatch
opatch rollback -id 34672698
# Verify rollback
opatch lspatches
# 34672698 should no longer appear
Section 10: Apply DB RU 39034528
cd /<patch_staging_dir>/APR_2026/39034528
# Apply (interactive)
$ORACLE_HOME/OPatch/opatch apply
# OR apply silently (for automation/scripts)
$ORACLE_HOME/OPatch/opatch apply -silent
Note on oradism permissions: In some environments the DB RU apply (opatch apply) itself automatically changes oradism ownership from root:oinstall to oracle:oinstall with permissions 755. Always check after applying the DB RU before attempting any manual root steps:
ls -la $ORACLE_HOME/bin/oradism
If oradism is already showing oracle:755 post-apply, the manual chown/chmod steps are not required. Only apply root permission changes if oradism remains root-owned after the patch apply.
Section 11: Apply OJVM RU 38906621
cd /<patch_staging_dir>/APR_2026/38906621
$ORACLE_HOME/OPatch/opatch apply
11.1 Verify OPatch inventory after apply
$ORACLE_HOME/OPatch/opatch lspatches
Patch ID
Description
38906621
OJVM RELEASE UPDATE: 19.31.0.0.260421
39034528
Database Release Update: 19.31.0.0.260421
29585399
OCW RELEASE UPDATE 19.3.0.0.0
Note: Patch 34672698 no longer appears — successfully rolled back. Patch 34672698’s fix is now natively bundled into the 19.31 RU.
Section 12: Start Database and Listener
sqlplus / as sysdba
SQL> startup;
SQL> alter pluggable database all open;
SQL> show pdbs;
SQL> exit;
lsnrctl start LISTENER
lsnrctl status LISTENER
Section 13: Run Datapatch
Datapatch applies the SQL-level changes for the new patches into the CDB and all open PDBs. This step is mandatory — OPatch alone does not apply SQL changes.
cd $ORACLE_HOME/OPatch
# Run datapatch and capture output to log
./datapatch -verbose | tee /tmp/datapatch_19_31.log
Note on tee and exit codes: Using tee in a pipeline means the shell reports the exit code of tee, not datapatch. If you need to capture datapatch’s actual exit code for scripted validation, use: ./datapatch -verbose > /tmp/datapatch_19_31.log 2>&1; echo "Exit code: $?"
Section 14: Post-Patch Validation
14.1 Verify dba_registry_sqlpatch (post-patch)
SQL> select patch_id, action, status, action_time, description
2 from dba_registry_sqlpatch
3 order by action_time;
PATCH_ID
ACTION
STATUS
ACTION_TIME
DESCRIPTION
34672698
ROLLBACK
SUCCESS
17-MAY-26 09.12.43 AM
ORA-00800 fix (overlay)
38523609
ROLLBACK
SUCCESS
17-MAY-26 09.15.02 AM
OJVM Release Update 19.30
39034528
APPLY
SUCCESS
17-MAY-26 09.21.18 AM
Database Release Update 19.31.0.0.260421
38906621
APPLY
SUCCESS
17-MAY-26 09.25.47 AM
OJVM Release Update 19.31.0.0.260421
14.2 Verify dba_registry (post-patch)
SQL> select comp_id, comp_name, version, status from dba_registry order by comp_id;
COMP_ID
COMP_NAME
VERSION
STATUS
APS
OLAP Analytic Workspace
19.0.0.0.0
VALID
CATALOG
Oracle Database Catalog Views
19.0.0.0.0
VALID
CATJAVA
Oracle Database Java Packages
19.0.0.0.0
VALID
CATPROC
Oracle Database Packages and Types
19.0.0.0.0
VALID
CONTEXT
Oracle Text
19.0.0.0.0
VALID
DV
Oracle Database Vault
19.0.0.0.0
VALID
JAVAVM
JServer JAVA Virtual Machine
19.0.0.0.0
VALID
OLS
Oracle Label Security
19.0.0.0.0
VALID
ORDIM
Oracle Multimedia
19.0.0.0.0
VALID
OWM
Oracle Workspace Manager
19.0.0.0.0
VALID
SDO
Spatial
19.0.0.0.0
VALID
XDB
Oracle XML Database
19.0.0.0.0
VALID
XML
Oracle XDK
19.0.0.0.0
VALID
XOQ
Oracle OLAP API
19.0.0.0.0
VALID
14.3 Verify version
SQL> select version_full from v$instance;
VERSION_FULL
-----------------
19.31.0.0.0
14.4 Check invalid objects (post-patch)
SQL> select count(*) from dba_objects where status = 'INVALID';
COUNT(*)
----------
8
Invalid count unchanged from baseline — no new invalids introduced by patching.
14.5 Run utlrp.sql (conditional)
Note: Running utlrp.sql is conditional — only required if you see new invalid objects that were not in the baseline count, or if Oracle-supplied objects remain INVALID after 24 hours. It is not mandatory after every patch apply.
-- Only run if new invalids are found
sqlplus / as sysdba
SQL> @?/rdbms/admin/utlrp.sql
14.6 CDB-aware validation (cdb_registry_sqlpatch)
-- Run from CDB$ROOT to validate across all containers
SQL> col con_id format 99
SQL> col patch_id format 9999999999
SQL> col status format a10
SQL> col action format a10
SQL> select con_id, patch_id, action, status, description
2 from cdb_registry_sqlpatch
3 order by con_id, action_time;
Note:cdb_registry_sqlpatch shows the patch apply status for each container (CDB$ROOT and all PDBs). Use this view to confirm datapatch applied successfully in all PDBs, not just the root container. All rows should show SUCCESS.
Section 15: Rollback Procedure (If Required)
If the patch causes critical issues post-apply, roll back in reverse order: OJVM first, then DB RU. This requires full database downtime.
opatch lspatches
-- Expect to see 38632161 and 38523609 back as active
15.5 Start database, open PDBs, run datapatch
sqlplus / as sysdba
SQL> startup;
SQL> alter pluggable database all open;
SQL> show pdbs;
SQL> exit;
cd $ORACLE_HOME/OPatch
./datapatch -verbose
Important: If the Oracle Home is severely corrupted, restore from the Oracle Home TAR backup taken in Section 6, then run datapatch. If database files are also affected, restore and recover from the RMAN backup first, then run datapatch. Never skip datapatch after a rollback — the SQL layer must be consistent with the binary layer.
Section 16: Common Failures and Troubleshooting
ZOP-47: Conflict with existing overlay patch
Symptom:opatch prereq CheckConflictAgainstOHWithDetail reports a conflict between an installed overlay patch and the new RU.
Cause: The overlay patch fix has been natively absorbed into the new RU.
Resolution: Roll back the overlay patch using opatch rollback -id <patch_id>, then proceed with the RU apply. Confirm with Oracle Support SR if unsure whether the conflict is a ZOP-47 (self-resolving) or a genuine incompatibility.
ZOP-40: Patch already installed
Symptom: OPatch reports the patch is already present in the inventory.
Cause: Patch was applied in a previous cycle or the inventory is stale.
Resolution: Verify with opatch lspatches. If genuinely already installed, no action needed. If inventory is stale, run opatch lsinventory -detail to diagnose.
Datapatch skipped PDB
Symptom: Datapatch log shows one or more PDBs were skipped.
Cause: PDB was not open during datapatch execution.
Resolution: Open the skipped PDB (alter pluggable database <pdb_name> open;) and re-run datapatch. Confirm via cdb_registry_sqlpatch that all containers show SUCCESS.
OPatch heap space error
Symptom: OPatch fails with a Java heap space error during apply.
Cause: Default JVM heap is insufficient for large patch sets.
Resolution: Set JAVA_TOOL_OPTIONS="-Xmx512m" before running opatch. This is environment-specific and not universally required — only apply if the error is observed.
OPatch inventory lock
Symptom: OPatch fails reporting the inventory is locked by another process.
Cause: A previous OPatch run did not exit cleanly, leaving a lock file.
Resolution: Check for and remove the lock file: $ORACLE_HOME/.patch_storage/<lock_file>. Confirm no other OPatch process is running before removing.
Key MOS References
Document
Description
Doc ID CPU58
Critical Patch Update (CPU) Apr 2026 for Oracle Database Products — patch availability, known issues, delayed RU tracking
Doc ID KB106822
Primary Note for Database Quarterly Release Updates — recommended reading before any RU apply
Doc ID KB869205
Oracle Database 19c Apr 2026 RU Known Issues
Doc ID KB137197
OJVM Conditional Rolling Install Details
Doc ID 244241.1
OPatch Support for RAC Rolling Patches
Doc ID 293369.1
OPatch Documentation List
Disclaimer: All server names, hostnames, database names, and environment-specific paths in this post have been anonymized. Steps and outputs are based on real production experience adapted for general use. Always test in a non-production environment before applying patches to production systems.
If you found this useful, connect with me on LinkedIn or explore more Oracle DBA scripts on my GitHub. More patching walkthroughs at syedanwarahmedoracle.blog.
Share Your Experience
Have you applied Oracle 19c CPU patches and hit a similar overlay patch conflict? Or found that a fix you applied months ago is now natively bundled into the next RU? Your real-world experiences help fellow DBAs tackle the same challenges. Drop a comment below — questions, observations, and feedback are always welcome.
It started with an RMAN failure at 22:30. The backup script for DBPRO02 fired on schedule and died within 2 minutes. The RMAN log told the story clearly:
RMAN-03009: failure of backup command on c4 channel at 22:32:13
ORA-19502: write error on file "/dbbackup/DBPRC02/rman/DiffInc_DBPRC02_4u4mi8bf"
ORA-27072: File I/O error
Additional information: 4
Three more channels followed — c1, c2, c3 — all crashing at exactly 22:32:48. When multiple channels fail simultaneously at the same timestamp, it almost always means one thing: the destination filesystem just hit 100%.
What we did not know yet — digging into backup history would reveal that DBPRO01 had already failed at 12:30 that same day for the same reason, 10 hours earlier. Two databases unprotected on the same night.
2. The Investigation — Folder by Folder
The first step was understanding what was consuming the disk. One command gave us the top-level picture:
744G inside DBPRC01 alone — 73% of the entire disk. That was our primary suspect.
Drilling into DBPRC01
$ du -sh /dbbackup/DBPRC01/rman/* | sort -rh | head -10
7.5G DiffInc_DBPRC01_fl4lumij
7.5G DiffInc_DBPRC01_eg4lrnh5
7.5G DiffInc_DBPRC01_b44lc7uh
...
Every single file was a DiffInc_ or ArchivelogAll_ backup piece. No variety. No cleanup. Just backup after backup piling up.
$ ls /dbbackup/DBPRC01/rman/ | wc -l
1522
1,522 backup pieces. We checked the oldest and newest:
Oldest file on disk: 2022-05-07
Newest file on disk: 2026-04-25
Four years of backup files on disk — or so we thought.
3. The RMAN Investigation — Where Things Got Interesting
We connected RMAN to the database and ran the retention check:
RMAN> SHOW RETENTION POLICY;
CONFIGURE RETENTION POLICY TO REDUNDANCY 30;
REDUNDANCY 30. This tells RMAN to keep the last 30 complete backup copies of every datafile before considering anything obsolete.
Next logical step — check what RMAN considers obsolete:
RMAN> REPORT OBSOLETE;
no obsolete backups found
Nothing? With 1,522 files on disk?
We ran CROSSCHECK BACKUP — all 1,693 objects came back AVAILABLE. Then we checked the actual date range RMAN was tracking from the database control file:
SELECT TO_CHAR(MIN(completion_time),'DD-MON-YYYY') oldest,
TO_CHAR(MAX(completion_time),'DD-MON-YYYY') newest,
COUNT(*) total_pieces
FROM v$backup_piece_details
WHERE status = 'A';
OLDEST NEWEST TOTAL_PIECES
03-DEC-2025 25-APR-2026 1541
The control file only tracks pieces from December 2025 onwards — about 5 months. The 2022/2023 files seen on disk were old directories and scripts, not backup pieces. All 1,541 current pieces were legitimate and RMAN considered every one of them necessary under REDUNDANCY 30.
This was the key insight: RMAN was not broken. The retention policy itself was the problem.
4. Root Cause — The Architecture Trap
The deeper investigation revealed something unexpected. Looking at the actual RMAN backup script:
connect target rman/password@DBPRO01
...
format '/dbbackup/DBPRC01/rman/DiffInc_%d_%u'
(database);
...
delete obsolete;
DBPRO01 (the production database) was backing up INTO the DBPRC01 directory. The directory names suggested one database but contained another database’s backups entirely. The naming convention was PRO to PRC — production database backups stored in the production-copy directory.
This pattern existed for all three database pairs on the server. Each production database backed up into its corresponding copy directory.
The delete obsolete command was in the script — but with REDUNDANCY 30 and weekly Level 0 backups, obsolete only kicks in after 30 complete Level 0 cycles. That is 30 weeks = 7.5 months of retention. Since the current tracking window was only 5 months, delete obsolete ran every night and found absolutely nothing to delete.
The math:
Retention policy
REDUNDANCY 30
Level 0 frequency
Weekly (Sundays)
Effective retention period
~30 weeks / 7.5 months
Backup tracking since
December 2025 (~5 months)
Result
delete obsolete finds nothing — ever
Daily backup size
~7–7.5G per run
Total accumulated
744G
Adding fuel to the fire — the patching activity on April 18 triggered an extra Level 0 backup, followed by the regular Sunday Level 0 on April 19. Two large Level 0 runs (~27G each) within 24 hours wrote the final ~54G that pushed the disk over the edge.
5. Secondary Findings During Investigation
OCT2025 CPU Patch Artifacts (7.7G)
The October 2025 CPU patch files (zip archives + extracted directories) were still sitting in /dbbackup/OCT2025_CPU/. A quick OPatch check confirmed the database had since been patched to 19.30 (January 2026 RU) — the October 2025 patches were fully superseded and rolled back from inventory. Safe to delete immediately.
$ $ORACLE_HOME/OPatch/opatch lsinventory | grep -E "38291812|38194382"
# Empty — neither Oct 2025 patch in inventory anymore
5-Year-Old Pre-Migration Export Dumps
Three directories contained Oracle 11.2.0.4 export dumps from January–March 2021 — taken before the migration to 19c. With the database now running 19.30, these had zero recovery value but occupied ~14G collectively. Flagged for manager approval before deletion.
Recovery Catalog Version Mismatch
The original RMAN log flagged this warning:
PL/SQL package RMAN.DBMS_RCVCAT version 19.11.00.00 in RCVCAT database is not current
PL/SQL package RMAN.DBMS_RCVMAN version 19.11.00.00 in RCVCAT database is not current
The recovery catalog is running 19.11 packages while the RMAN client is now 19.30. Non-critical tonight but requires UPGRADE CATALOG in the next maintenance window.
6. The Fix — Emergency Space Recovery
With management approval obtained, we executed a time-based delete — keeping the last 30 days of backups and removing everything older:
RMAN> DELETE NOPROMPT BACKUP COMPLETED BEFORE 'SYSDATE-30';
This command does three things atomically:
Queries catalog/controlfile for all pieces completed before the cutoff date
Deletes the physical files from disk
Removes the records from RMAN catalog — no orphaned entries, no catalog drift
Both failed backups were re-submitted immediately and ran successfully in parallel:
$ nohup sh /opt/oracle/scripts/rman/rman_backup_DBPRO01.sh &
$ nohup sh /opt/oracle/scripts/rman/rman_backup_DBPRO02.sh &
$ jobs -l
[1] Running nohup sh ...rman_backup_DBPRO01.sh &
[2] Running nohup sh ...rman_backup_DBPRO02.sh &
DBPRO02 Level 0 backup fails — same errors, all 4 channels
23:08
Investigation begins — df -hP /dbbackup confirms 100% full
23:15
DBPRC01 directory identified as 744G consumer
23:25
RMAN connected — REDUNDANCY 30 discovered
23:35
Architecture confirmed — PRO databases backing up into PRC directories
23:45
Root cause confirmed — 7.5-month retention, delete obsolete finds nothing
23:50
DELETE BACKUP COMPLETED BEFORE SYSDATE-30 executed
23:51
1,072 pieces deleted — disk drops to 53%
23:55
OCT2025_CPU removed — disk at 52%, 495G free
00:00
Both backup jobs re-submitted and running successfully
8. Permanent Fix Recommendations
Fix 1 — Change Retention Policy to RECOVERY WINDOW
RMAN> CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 14 DAYS;
REDUNDANCY 30 with weekly Level 0s means 7.5 months of retention — far beyond what any production SLA requires. A 14-day recovery window keeps 2 weeks of backups regardless of backup frequency, and delete obsolete will actually find and remove old pieces going forward.
Fix 2 — Add Pre-Backup Space Check to Cron Script
#!/bin/bash
BACKUP_FS="/dbbackup"
THRESHOLD=20
AVAIL_PCT=$(df -hP $BACKUP_FS | awk 'NR==2 {gsub(/%/,""); print 100-$5}')
if [ "$AVAIL_PCT" -lt "$THRESHOLD" ]; then
echo "ABORT: $BACKUP_FS is ${AVAIL_PCT}% free — below ${THRESHOLD}% threshold" \
| mailx -s "BACKUP ABORTED: Low space on $BACKUP_FS" $MAILTO
exit 1
fi
A failing backup that writes 3G before dying is worse than a backup that never starts — it wastes the last 3G of free space and leaves partial pieces on disk.
Fix 3 — Upgrade the Recovery Catalog
RMAN> CONNECT TARGET /
RMAN> CONNECT CATALOG rman/password@rmancat
RMAN> UPGRADE CATALOG;
RMAN> UPGRADE CATALOG; -- run twice as prompted
The catalog is 2 major patch levels behind the RMAN client. Some catalog-dependent operations will start failing if left unaddressed.
Fix 4 — Filesystem Monitoring Alert
The FRA check scripts already email on FRA usage above 80%. The same pattern should exist for /dbbackup. A simple cron entry checking disk usage every hour with alert at 80% would have caught this days before the disk hit 100%.
9. Key Takeaways for Oracle DBAs
REDUNDANCY N is not always safer than RECOVERY WINDOW. REDUNDANCY 30 with weekly Level 0 backups means 7.5 months of retention — likely far beyond your RPO requirement and a silent space accumulator.
Always verify what delete obsolete actually deletes. If it finds nothing to delete every single night, that is a warning sign — not reassurance.
Check backup naming conventions carefully. When a directory named DBPRC01 contains DBPRO01 backups, retention policies applied to the wrong database RMAN configuration control the cleanup behavior.
Patching days generate oversized backups. A Level 0 taken manually on patch day plus the regular Sunday Level 0 the next day equals 2x the normal space consumption in 24 hours. Ensure extra headroom exists going into patch windows.
Use DELETE BACKUP COMPLETED BEFORE SYSDATE-N for emergency cleanup — not OS-level rm. RMAN deletes atomically update both the physical files and the catalog, preventing expired/orphaned piece confusion later.
Never use rm on RMAN backup pieces directly unless you follow up with CROSSCHECK BACKUP and DELETE EXPIRED BACKUP to sync the catalog.
10. Commands Reference — Quick Cheat Sheet
-- Check retention policy
RMAN> SHOW RETENTION POLICY;
-- Preview what would be deleted (dry run)
RMAN> REPORT OBSOLETE;
RMAN> LIST BACKUP COMPLETED BEFORE 'SYSDATE-30';
-- Emergency cleanup — delete pieces older than 30 days
RMAN> DELETE NOPROMPT BACKUP COMPLETED BEFORE 'SYSDATE-30';
-- Standard cleanup based on retention policy
RMAN> DELETE NOPROMPT OBSOLETE;
-- Sync catalog after any OS-level file operations
RMAN> CROSSCHECK BACKUP;
RMAN> DELETE NOPROMPT EXPIRED BACKUP;
-- Change to time-based retention (recommended)
RMAN> CONFIGURE RETENTION POLICY TO RECOVERY WINDOW OF 14 DAYS;
-- Check backup piece date range in control file
SELECT TO_CHAR(MIN(completion_time),'DD-MON-YYYY') oldest,
TO_CHAR(MAX(completion_time),'DD-MON-YYYY') newest,
COUNT(*) total_pieces
FROM v$backup_piece_details
WHERE status = 'A';
-- Check backup history
SELECT session_key, input_type, status,
TO_CHAR(start_time,'YYYY-MM-DD HH24:MI:SS') start_time,
output_bytes_display, time_taken_display
FROM v$rman_backup_job_details
ORDER BY start_time DESC;
Conclusion
What appeared to be a simple disk full incident turned out to involve a multi-database backup architecture, a misconfigured retention policy, and a cleanup mechanism that was technically running correctly but never finding anything to clean. The fix itself — one RMAN command — took under 5 minutes. The real work was the systematic investigation to understand exactly what was safe to delete and why.
That is Oracle DBA work in a nutshell: the fix is often simple; understanding why it is safe to run is the real job.
If you found this useful, connect with me on LinkedIn or explore more Oracle DBA scripts on my GitHub. More incident walkthroughs at syedanwarahmedoracle.blog.
In a GoldenGate 19c environment, one of the EXTRACT processes abended with a TCP/IP error while all other EXTRACT processes were running normally. This post walks through the exact error, the diagnosis process, and how the issue was resolved — along with preventive recommendations to avoid recurrence.
Environment Details
Component
Detail
GoldenGate Version
Oracle GoldenGate 19c (Classic Architecture)
Source Database
Oracle 12.1.0.2
Source Server
ogg-source01.example.com
Manager Port
8910
Abended Process
EXTRACT EXTR0001
The Problem
During routine monitoring, the GoldenGate process status showed:
“Connection refused” (errno 79) means the TCP SYN packet reached the remote server but the connection could not be completed — a transient error commonly seen in GoldenGate environments.
Diagnosis Steps
Step 1 — View the EXTRACT Report
The first step was to read the report file and identify the exact error:
GGSCI> VIEW REPORT EXTR0001
The error pointed to OGG-01224 TCP/IP error 79 (Connection refused) at the opt_negotiate stage — meaning the EXTRACT successfully read the source redo logs but failed when attempting to communicate with the remote GoldenGate Manager.
Step 2 — Check EXTRACT Status
GGSCI> INFO EXTRACT EXTR0001, DETAIL
GGSCI> INFO ALL
This confirmed only EXTR0001 had abended while the remaining EXTRACT processes were running normally.
Root Cause
OGG-01224 TCP/IP errors are among the most commonly encountered errors in GoldenGate environments. They are often transient in nature — caused by brief network interruptions, a momentary spike in system load, or a short-lived instability in the GoldenGate Manager communication layer. In many cases, no deep investigation is required; the EXTRACT simply needs to be restarted after waiting a short period for the underlying condition to clear.
The other EXTRACT processes (EXTR0002, EXTR0003, EXTR0004) were unaffected, confirming the issue was isolated and transient.
Resolution
After waiting briefly for the transient condition to clear, the abended EXTRACT was restarted:
GGSCI> START EXTRACT EXTR0001
GGSCI> INFO EXTRACT EXTR0001, DETAIL
The EXTRACT came up immediately and resumed processing without any data loss or position reset.
Post-Recovery Verification
-- Confirm all processes running
GGSCI> INFO ALL
-- Check lag is reducing
GGSCI> LAG EXTRACT EXTR0001
-- Confirm records are flowing
GGSCI> STATS EXTRACT EXTR0001
Key Takeaways
1. OGG-01224 TCP/IP errors are common and often transient.
These errors are frequently seen in GoldenGate environments and do not always indicate a serious underlying problem. A brief network hiccup, momentary system load, or a short instability in the Manager communication layer can trigger this error. In many cases, waiting a short time and restarting the EXTRACT is all that is needed.
2. The opt_negotiate method is the first GoldenGate-level handshake.
After the TCP connection is established, GoldenGate negotiates capabilities via opt_negotiate. A failure at this stage is typically transient and clears on its own after a retry.
3. Multiple EXTRACTs can fail independently.
When one EXTRACT abends while others continue running, the issue is isolated. Do not assume a global outage — check each process individually.
4. Add AUTORESTART to Manager params.
To reduce downtime on similar future occurrences, configure Manager to auto-restart key processes:
[ ] View EXTRACT report: GGSCI> VIEW REPORT <extract_name>
[ ] Note the SourceMethod — is it opt_negotiate or during data transfer?
[ ] Check if other EXTRACTs are running (INFO ALL)
[ ] If error is transient: wait briefly, then START EXTRACT <name>
[ ] If error persists: test port reachability (telnet <rmthost> <mgrport>)
[ ] If error persists beyond a retry: escalate for network/firewall checks
[ ] Verify RMTHOST/MGRPORT in EXTRACT param file (VIEW PARAMS <name>)
[ ] Check for encryption mismatch if opt_negotiate consistently fails
[ ] Add AUTORESTART in mgr.prm to handle future transient failures automatically
MOS Note 966227.1: OGG Troubleshooting TCP/IP Errors In Open Systems (My Oracle Support login required)
About the Author
Syed Anwar Ahmed is an Oracle Apps DBA with extensive production experience managing Oracle RAC, E-Business Suite, and GoldenGate replication environments. He shares practical DBA knowledge from real-world incidents on his blog and is an Oracle ACE Apprentice candidate.