Author: SYED ANWAR AHMED

  • Oracle Database 19c July 2026 RU Patching – Upgrade from 19.31 to 19.32 on Linux

    الحمد لله — This post documents a real Oracle Database 19c patching activity on a Linux x86-64 environment, progressing from 19.31 to 19.32 using the July 2026 Combo Patch. The activity included precheck validation, binary patching, datapatch execution, and post-patch verification with actual command outputs.

    Introduction

    This article documents the steps followed to patch an Oracle Database 19c environment from 19.31 to 19.32 using the July 2026 Database and OJVM Release Update Combo Patch on Linux x86-64.

    The patches applied were:

    Patch 39618649
    Combo of OJVM Component Release Update 19.32.0.0.260721
    + Database Jul 2026 Release Update 19.32.0.0.260721
    
    Component patches:
    39472050 - Database Release Update 19.32.0.0.260721
    39222882 - Oracle JavaVM Component Release Update 19.32.0.0.260721

    Pre-Patching Validation

    1. Verify Current Database Patch Level

    First, verify the current patches installed in the Oracle Home:

    $ $ORACLE_HOME/OPatch/opatch lspatches

    The environment was running the following baseline before July patching:

    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

    Patching objective:

    • Database RU: 19.31 → 19.32 (39034528 → 39472050)
    • OJVM RU: 19.31 → 19.32 (38906621 → 39222882)

    6. Run DB RU Conflict Check

    Before applying the Database RU, validate for conflicting one-off patches:

    $ cd 39618649/39472050
    $ opatch prereq CheckConflictAgainstOHWithDetail -ph ./
    
    Prereq "checkConflictAgainstOHWithDetail" passed.
    
    OPatch succeeded.

    ✓ No conflicting one-off patch was identified for the DB RU 39472050.

    7. Run OJVM RU Conflict Check

    Perform the same validation for the OJVM RU:

    $ cd 39618649/39222882
    $ opatch prereq CheckConflictAgainstOHWithDetail -ph ./
    
    Prereq "checkConflictAgainstOHWithDetail" passed.
    
    OPatch succeeded.

    ✓ No conflicting one-off patch was identified for the OJVM RU 39222882.

    Post-Patching Results

    OPatch Inventory Verification

    $ $ORACLE_HOME/OPatch/opatch lspatches
    
    39222882;OJVM RELEASE UPDATE: 19.32.0.0.260721
    39472050;Database Release Update : 19.32.0.0.260721
    29585399;OCW RELEASE UPDATE 19.3.0.0.0
    
    OPatch succeeded.

    Patch progression:

    • OJVM RU: 38906621 (19.31) → 39222882 (19.32) ✓
    • Database RU: 39034528 (19.31) → 39472050 (19.32) ✓
    • OCW: 29585399 (unchanged) ✓

    Component Registry Status

    All major database components are VALID after patching:

    CATALOG         VALID
    CATEGORY        VALID
    CATJAVA         VALID
    CATOJAVA        VALID
    JAVAVM          VALID
    XML             VALID
    XDB             VALID

    Database and PDB Status

    SQL> select name, open_mode, database_role from v$database;
    
    NAME            OPEN_MODE       DATABASE_ROLE
    ----------      -----------     ----------------
    PRODDB          READ WRITE      PRIMARY
    
    SQL> show pdbs;
    
        CON_ID CON_NAME                 OPEN_MODE  RESTRICTED
    ---------- ---------------------- ---------- ----------
             2 PDB$SEED                 READ ONLY  NO
             3 APPPDB                   READ WRITE NO
             4 ARCHPDB                  READ WRITE NO

    ✓ Database and all PDBs are open and operational.

    SQL Patch Registry

    Both 19.32 patches applied successfully to CDB and APPPDB PDB:

    PATCH_ID      ACTION     STATUS          DESCRIPTION
    -----------   ---------- --------------- ----------------------------------
    39222882      APPLY      SUCCESS         OJVM RU 19.32.0.0.260721
    39472050      APPLY      SUCCESS         Database RU 19.32.0.0.260721

    Note: During the 19.32 OJVM update, the previous 19.31 OJVM patch (38906621) may be recorded as ROLLBACK SUCCESS, followed by successful application of OJVM RU 39222882. This is normal and expected during the RU transition — it is not an indication of a patching failure.

    Invalid Objects Validation

    Post-patch invalid objects count: 83 (unchanged from pre-patch baseline)

    ✓ Invalid object count matches pre-patch baseline. No new invalids introduced by 19.32 RU.

    oradism Permissions Restored

    $ ls -l $ORACLE_HOME/bin/oradism
    -rwsr-x---. 1 root oinstall 147848 May  5 12:25 /u01/app/oracle/product/19c/db_1/bin/oradism

    ✓ Permissions correctly restored to original state (root:oinstall 4750).

    Rollback Plan

    Important: Before executing any rollback, always:

    • Verify the exact version from the patch component README
    • Follow the documented rollback sequence from the README
    • Test in a non-production environment first
    • Obtain explicit approval from your database administration team or management

    If the 19.32 RU must be rolled back, the procedure generally follows this sequence (consult the specific README for patches 39472050 and 39222882 for the authoritative step-by-step guidance):

    1. Shut down the database and listener
    2. Revert the OJVM RU first, then the Database RU (reverse order of application)
    3. Start the database in restricted mode to verify compatibility
    4. Run datapatch to revert SQL patches: ./datapatch -verbose
    5. Verify patch registry and OPatch inventory reflect the reverted state
    6. Open the database normally and run post-revert health checks

    Always retain a clean, tested backup of the Oracle Home taken immediately before patching. This allows for rapid restoration if rollback becomes necessary.

    Key Takeaways

    A reliable Oracle Database RU patching activity is more than running opatch apply twice. A structured approach includes:

    • Capture existing state: Patch inventory, SQL patch registry, invalid objects, database role, PDB status, and oradism permissions before making any changes.
    • Validate prerequisites: Run conflict checks on both component patches and ensure OPatch is at the recommended version.
    • Follow the documented sequence: Binary patches in the correct order, then startup, then datapatch.
    • Verify at each step: Check OPatch inventory after binary patching, verify component registry after startup, and confirm datapatch success via the SQL registry.
    • Compare against baseline: Invalid objects, registry status, and listener configuration should match pre-patch evidence.
    • Plan for rollback: Maintain a clean backup and understand the documented rollback procedure from the official patch README before starting.

    Oracle Database 19c was successfully upgraded from the 19.31 RU level to the July 2026 19.32 RU level, including OJVM, with successful binary patching, SQL patching, and post-patch health validation.

  • Oracle EBS WebLogic: Troubleshooting Security:090220 Rule 2 Connection Rejection

    You receive a call: WebLogic requests are being rejected.

    The error appears in the logs:

    [Socket:000445]Connection rejected, filter blocked Socket,
    weblogic.security.net.FilterException:
    [Security:090220]rule 2

    At first, you might start checking WebLogic authentication, AdminServer credentials, or database connectivity.

    Don’t start there.

    The exception is already giving you a very strong clue about where to investigate.

    The Error Tells the Story

    The important part is:

    weblogic.security.net.FilterException
    [Security:090220]rule 2

    Oracle documents BEA-090220 as:

    A connection filter rule denied access.

    In other words, WebLogic is rejecting the connection because of a configured connection-filter rule.

    This is different from an authentication failure.

    The request is reaching the WebLogic server, and the connection filter is evaluating the incoming connection.

    What Is a WebLogic Connection Filter?

    WebLogic connection filters provide a network-level access-control layer.

    They can be used to control which clients are permitted to connect to a WebLogic Server instance. Oracle describes connection filters as an additional security layer that can restrict access at the network level.

    A simplified flow looks like this:

    Client
       |
       v
    WebLogic Listener
       |
       v
    Connection Filter
       |
       +---- ALLOW ----> Continue processing
       |
       +---- DENY -----> Connection rejected

    This is why an error such as:

    Connection rejected, filter blocked Socket

    is so useful during troubleshooting.

    Understanding Security:090220 Rule 2

    This is where we need to be precise.

    WebLogic evaluates connection-filter rules in the order in which they are configured.

    The first matching rule determines whether the connection is allowed or denied. If no rule matches, the connection is permitted by the default filter implementation.

    For example:

    Rule 1: 192.0.2.11 * * allow
    Rule 2: 192.0.2.12 * * allow
    Rule 3: 0.0.0.0/0 * * deny

    If a connection matches Rule 3, WebLogic reports:

    [Security:090220]rule 3

    Therefore:

    rule 2 identifies the connection-filter rule associated with the rejection reported by WebLogic. Always inspect the actual configured rule sequence rather than assuming it is the catch-all deny rule.

    Where Is the Configuration?

    For Oracle E-Business Suite Release 12.2, the WebLogic connection-filter configuration can be found in:

    $DOMAIN_HOME/config/config.xml

    Oracle’s EBS documentation shows the connection-filter configuration in this file.

    For example:

    <connection-filter>weblogic.security.net.ConnectionFilterImpl</connection-filter>
    
    <connection-filter-rule>192.0.2.11 * * allow</connection-filter-rule>
    
    <connection-filter-rule>192.0.2.12 * * allow</connection-filter-rule>
    
    <connection-filter-rule>0.0.0.0/0 * * deny</connection-filter-rule>

    In an EBS environment you may also see an EBS-specific connection-filter implementation, such as:

    <connection-filter>oracle.apps.ad.tools.configuration.wls.filter.EBSConnectionFilterImpl</connection-filter>

    If that is what your environment is using, don’t replace the filter class just because you are troubleshooting a rule. Focus on the configured rules.

    Check the Rules Before Changing Anything

    First inspect the actual configuration:

    cd $DOMAIN_HOME/config
    
    grep -n -A10 -B5 "connection-filter" config.xml

    You want to establish:

    • Which connection-filter implementation is configured?
    • What are the connection-filter rules?
    • Which source IP is making the connection?
    • Which rule is actually denying the connection?
    • Is the source IP supposed to be allowed?

    This is much better than immediately changing the filter to allow.

    Always Back Up config.xml

    Before making any manual configuration change:

    cd $DOMAIN_HOME/config
    
    cp -ip config.xml config.xml_$(date +%Y%m%d_%H%M%S).bak

    If this is an EBS environment, also consider the run/patch filesystem implications before making a change that needs to persist through online patching. Oracle’s EBS documentation notes that configuration changes on the run filesystem are propagated to the patch filesystem during fs_clone.

    Don’t Confuse This With Basic Authentication

    While reviewing config.xml, you may encounter:

    <enforce-valid-basic-auth-credentials>
        false
    </enforce-valid-basic-auth-credentials>

    It is tempting to associate this with every WebLogic security-related error.

    But BEA-090220 specifically indicates that a connection-filter rule denied access.

    Therefore, for this particular error, the first investigation should be:

    Security:090220
           ↓
    Connection Filter
           ↓
    Connection Filter Rules
           ↓
    Source IP / Port / Protocol

    rather than Basic Authentication.

    A Typical Configuration

    Oracle’s EBS documentation provides examples such as:

    <connection-filter-rule>192.0.2.11 * * allow</connection-filter-rule>
    
    <connection-filter-rule>192.0.2.12 * * allow</connection-filter-rule>
    
    <connection-filter-rule>192.0.2.100 * 17001 allow https</connection-filter-rule>
    
    <connection-filter-rule>0.0.0.0/0 * * deny</connection-filter-rule>

    The final rule:

    0.0.0.0/0 * * deny

    acts as a catch-all deny.

    Oracle specifically documents this pattern: allow the required sources first, then use 0.0.0.0/0 * * deny as the final rule so that all other sources are rejected.

    The Incident Scenario

    In our case, the error was:

    [Socket:000445]Connection rejected, filter blocked Socket,
    weblogic.security.net.FilterException:
    [Security:090220]rule 2

    During the investigation, we reviewed the WebLogic configuration and traced the rejection back to the connection-filter rules.

    The important distinction was:

    WebLogic is running
            |
            v
    Request reaches WebLogic
            |
            v
    Connection filter evaluates request
            |
            v
    Configured rule denies connection
            |
            v
    Security:090220

    That changed the direction of the investigation immediately.

    Resolution

    Once the offending rule has been identified, there are two approaches.

    Option 1 — Correct the Allow Rule

    This is the preferred approach.

    For example, if the trusted WebLogic client is:

    192.0.2.50

    the configuration could contain:

    <connection-filter-rule>192.0.2.50 * * allow</connection-filter-rule>
    
    <connection-filter-rule>0.0.0.0/0 * * deny</connection-filter-rule>

    The important point is that the allow rule must appear before the catch-all deny rule.

    Option 2 — Temporary Troubleshooting Workaround

    In a controlled troubleshooting situation, changing:

    0.0.0.0/0 * * deny

    to:

    0.0.0.0/0 * * allow

    can be used to confirm that the connection filter is the cause of the problem.

    However, this effectively removes the intended network restriction.

    So: Use this as a diagnostic/workaround step, not as the preferred permanent production configuration.

    Oracle’s documented security model is to allow the required sources and retain a final deny rule.

    If Manually Editing config.xml

    If you are manually modifying the domain configuration rather than using the WebLogic Administration Console, perform the change under your environment’s standard configuration-management procedure and ensure the Administration Server is not concurrently modifying the file.

    For a controlled manual change, the practical sequence is:

    Stop AdminServer
          |
          v
    Backup config.xml
          |
          v
    Review current connection-filter rules
          |
          v
    Make the required change
          |
          v
    Validate config.xml
          |
          v
    Start AdminServer
          |
          v
    Monitor logs
          |
          v
    Retest the connection

    Oracle also supports configuring connection filters through the WebLogic Administration Console and then activating the configuration and restarting as required.

    Troubleshooting Decision Tree

    When you encounter:

    [Socket:000445]Connection rejected, filter blocked Socket
    weblogic.security.net.FilterException
    [Security:090220]rule N

    use this approach:

                     WebLogic Request
                           |
                           v
                  WebLogic reachable?
                           |
                          YES
                           |
                           v
                 Check exact exception
                           |
                           v
              weblogic.security.net
                 FilterException?
                           |
                          YES
                           |
                           v
                  Security:090220?
                           |
                          YES
                           |
                           v
           Review connection-filter rules
                           |
                           v
              Identify rule N
                           |
                           v
            Identify source IP/port/protocol
                           |
                           v
              Does the rule allow it?
                     /          \
                   YES          NO
                    |            |
                    v            v
            Investigate next   Correct the
                 layer          filter rule

    Key Takeaways

    1. Read the exception literallyFilterException is a major clue.
    2. Security:090220 means a connection-filter rule denied access — Oracle explicitly documents BEA-090220 this way.
    3. Rule ordering matters — The first matching rule determines the result.
    4. rule 2 is not automatically the catch-all deny — Check the actual rule list before making assumptions.
    5. 0.0.0.0/0 * * deny is normally a catch-all restriction — It should generally come after the required allow rules.
    6. Don’t use a global allow as the permanent fix — If the issue is caused by a missing source IP rule, add the required source rather than permanently opening the filter.

    Final Thought

    One of the most useful troubleshooting skills in middleware administration is learning to read the error as a diagnostic path.

    Consider the original message:

    [Socket:000445]
    Connection rejected,
    filter blocked Socket,
    weblogic.security.net.FilterException:
    [Security:090220]rule 2

    Break it down:

    Connection rejected
            ↓
    Filter blocked Socket
            ↓
    WebLogic Connection Filter
            ↓
    BEA/Security 090220
            ↓
    Connection-filter rule denied access
            ↓
    Check the actual rule identified by the error

    Instead of treating it as another generic WebLogic failure, the error points us directly toward the connection-filter configuration.

    Read the error carefully. Sometimes the error message has already told you where the problem is.

  • Troubleshooting Oracle EBS R12.1 txkCfgUtlfileDir.pl: APPS Lockouts, Missing AppsUtil JRE & UTL_FILE_DIR Synchronization

    Alhamdulillah.

    Introduction

    While working on an Oracle E-Business Suite R12.1 environment running on Oracle Database 19c, I encountered a failure while configuring the database-side UTL_FILE_DIR replacement using txkCfgUtlfileDir.pl.

    The script repeatedly reported:

    ERRORMSG: Invalid APPS database user credentials.
    ERRORCODE = 1 ERRORCODE_END

    At first glance, this looked like a straightforward APPS password problem.

    It wasn’t.

    The investigation eventually uncovered two separate contributing factors:

    • Issue 1: The APPS account was being locked by repeated authentication attempts from an external application.
    • Issue 2: The TXK utility expected a JRE component that was missing from the AppsUtil directory structure.

    These were independent issues discovered during different phases of the troubleshooting investigation. After resolving the APPS lockout and verifying the JRE availability, the UTL_FILE_DIR synchronization proceeded to completion: directory objects → setUtlFileDir → syncUtlFileDir → FND_CONC_CLONE.SETUP_CLEAN → Database AutoConfig.

    This post documents the investigation and the commands used.

    1. Environment

    The database environment was:

    Oracle E-Business Suite R12.1
    Oracle Database 19c
    Database CDB : SRCDB
    PDB          : TESTDB
    Listener     : 1522
    Service      : ebs_service

    The ORACLE_HOME was:

    ORACLE_HOME=$ORACLE_BASE/TESTDB/db/tech_st/19.3.0.0

    The EBS database context file was:

    CONTEXT_FILE=$ORACLE_HOME/appsutil/TESTDB_dbctx.xml

    The environment was loaded with:

    cd $ORACLE_HOME
    . ./TESTDB_dbctx.env

    Validation:

    echo "ORACLE_SID=$ORACLE_SID"
    echo "ORACLE_HOME=$ORACLE_HOME"
    echo "TNS_ADMIN=$TNS_ADMIN"
    echo "CONTEXT_FILE=$CONTEXT_FILE"

    Expected values:

    ORACLE_SID=TESTDB
    ORACLE_HOME=$ORACLE_BASE/TESTDB/db/tech_st/19.3.0.0
    TNS_ADMIN=$ORACLE_HOME/network/admin/TESTDB_dbctx
    CONTEXT_FILE=$ORACLE_HOME/appsutil/TESTDB_dbctx.xml

    2. Initial txkCfgUtlfileDir.pl Failure

    The initial command was:

    perl $ORACLE_HOME/appsutil/bin/txkCfgUtlfileDir.pl \
    -contextfile=$CONTEXT_FILE \
    -oraclehome=$ORACLE_HOME \
    -outdir=$ORACLE_HOME/appsutil/log \
    -mode=getUtlFileDir

    The script failed with:

    FUNCTION: main::validateAppsSchemaCredentials
    ERRORMSG: Invalid APPS database user credentials.
    ERRORCODE = 1 ERRORCODE_END

    The context file itself was found successfully:

    Context file:
    $ORACLE_HOME/appsutil/TESTDB_dbctx.xml exists.

    So the failure was occurring during APPS credential validation.

    3. Validate the PDB

    Because this was a multitenant database, I first verified the PDB state.

    SHOW PDBS;

    Output:

    CON_ID CON_NAME    OPEN MODE   RESTRICTED
    ------ ----------- ----------- ----------
    2      PDB$SEED    READ ONLY   NO
    3      TESTDB      READ WRITE  NO

    Then:

    ALTER SESSION SET CONTAINER=TESTDB;

    The PDB was open read/write, so the next step was to validate the APPS account.

    4. APPS Account Investigation

    Initially, the APPS account showed as open after an unlock:

    ALTER USER APPS ACCOUNT UNLOCK;

    Verification:

    SELECT username,
           account_status,
           lock_date
    FROM dba_users
    WHERE username = 'APPS';

    At one point:

    APPS    OPEN

    However, the TXK utility continued to report invalid APPS credentials.

    The most useful clue came from the TXK-generated diagnostic log:

    cat $ORACLE_HOME/appsutil/log/TXK_UTIL_DIR_*/validate_apps_password.log

    The log showed:

    Connected to an idle instance.
    ERROR:
    ORA-28000: The account is locked.

    This was much more specific than the generic TXK error. The failure was occurring at the database authentication layer, not at the password validation level.

    5. Understanding the TXK Workflow

    Rather than treating the generic TXK error at face value, I inspected txkCfgUtlfileDir.pl to understand how the utility validates the APPS credentials.

    The script constructs the database connection using the APPS credentials from the EBS configuration and performs the validation through SQL*Plus. The detailed validate_apps_password.log showed ORA-28000, confirming that the failure was occurring at the database authentication layer.

    6. Verify Context Configuration

    The context file contained:

    grep -n -iE 's_apps_user|s_dbSid|s_tools_twotask|s_dbService' \
    $CONTEXT_FILE

    Relevant values:

    s_dbSid       = TESTDB
    s_dbService   = ebs_service
    s_apps_user   = apps

    The database SID, service and listener port were also checked in the context configuration:

    grep -n -iE 's_dbSid|s_dbService|s_dbport' \
    $CONTEXT_FILE

    Result:

    s_dbSid       = TESTDB
    s_dbService   = ebs_service
    s_dbport      = 1522

    These values were consistent with the environment.

    7. Verify TNS Connectivity

    The EBS TNS entry was properly configured for the PDB service.

    export TNS_ADMIN=$ORACLE_HOME/network/admin/TESTDB_dbctx

    Then:

    tnsping TESTDB

    Result:

    Used TNSNAMES adapter to resolve the alias
    Attempting to contact ...
    OK (0 msec)

    Therefore: TNS resolution and listener connectivity were not the problem.

    8. Important Discovery: APPS Account Was Being Locked

    At another point during testing, the account showed:

    USERNAME        ACCOUNT_STATUS
    --------------- -------------------------
    APPS            LOCKED(TIMED)

    The account was unlocked:

    ALTER USER APPS ACCOUNT UNLOCK;

    Then a direct connection succeeded:

    sqlplus apps@TESTDB
    
    SHOW USER;
    USER is "APPS"

    This confirmed that the APPS credentials being used for the direct SQL*Plus connection were valid at that point. However, subsequent attempts to run the TXK utility showed the account locked again:

    ORA-28000: The account is locked.

    This pattern indicated that something was repeatedly attempting authentication with invalid credentials, causing the account to lock after a number of failed attempts. The generic TXK error message “Invalid APPS database user credentials” did not hint at this root cause.

    9. SplashBI Was Contributing to the Lockout

    During the investigation, SplashBI sessions were identified as contributing to repeated APPS authentication attempts, and stale credentials were suspected.

    Those sessions were stopped/killed before continuing with the TXK procedure.

    This was a critical troubleshooting lesson:

    Do not repeatedly unlock APPS and rerun the TXK utility while another application is continuously attempting to authenticate with stale APPS credentials.

    Doing so simply creates another lockout cycle. After the interfering sessions were stopped, APPS authentication could be validated successfully, and the TXK procedure proceeded.

    10. A Second Prerequisite Issue: Missing AppsUtil JRE

    This was a separate issue discovered during the later stages of the TXK workflow. An earlier execution of syncUtlFileDir had shown:

    FUNCTION: main::getJavaCmd
    
    ERRORMSG:
    Invalid JDK TOP:
    $ORACLE_HOME/appsutil/jre/bin/java

    I checked:

    ls -ld $ORACLE_HOME/appsutil/jre

    and found:

    No such file or directory

    However, the AppsUtil clone area contained a valid JRE:

    ls -ld $ORACLE_HOME/appsutil/clone/jre

    and:

    ls -l $ORACLE_HOME/appsutil/clone/jre/bin/java

    The Java executable was present. Validation:

    $ORACLE_HOME/appsutil/clone/jre/bin/java -version
    
    java version "1.8.0_451"
    Java(TM) SE Runtime Environment

    11. Backup Before Correcting AppsUtil

    Before making the AppsUtil change, a backup was taken:

    cd $ORACLE_HOME/appsutil
    
    tar -czf $ORACLE_BASE/TESTDB/appsutil_pre_jre_fix_$(date +%Y%m%d_%H%M%S).tar.gz \
        TESTDB_dbctx.xml \
        txkSetCfgCDB.env \
        clone/jre

    The backup was verified:

    ls -lh $ORACLE_BASE/TESTDB/appsutil_pre_jre_fix_*.tar.gz
    
    -rw-r--r-- 1 oracle dba 46M Aug 18 11:32 appsutil_pre_jre_fix_20260818_113218.tar.gz

    This provided a rollback point before modifying the directory structure.

    12. Restore the Expected AppsUtil JRE

    The TXK source explicitly showed that getJavaCmd expected:

    $ORACLE_HOME/appsutil/jre/bin/java

    The existing valid JRE under clone/jre was copied to the expected location:

    cp -pR $ORACLE_HOME/appsutil/clone/jre \
           $ORACLE_HOME/appsutil/jre

    Validation:

    ls -ld $ORACLE_HOME/appsutil/jre
    ls -l $ORACLE_HOME/appsutil/jre/bin/java
    
    $ORACLE_HOME/appsutil/jre/bin/java -version
    java version "1.8.0_451"

    This restored the Java path expected by the TXK utility.

    13. Existing UTL_FILE_DIR Configuration

    An earlier execution had already generated:

    $ORACLE_HOME/dbs/TESTDB_utlfiledir.txt

    The original getUtlFileDir execution had failed during APPS credential validation when the account was locked. After resolving the APPS lockout and stopping the interfering authentication attempts, the TXK workflow was able to proceed. The existing configuration file from the previous successful run was available as a reference for the required filesystem paths.

    The paths included:

    $ORACLE_BASE/TESTDB/temp
    $APP_MOUNT/TESTDB/inst/apps/TESTDB_dbctx/appltmp
    $APP_MOUNT/TESTDB/apps/apps_st/comn/temp
    $ORACLE_BASE/TESTDB/db/tech_st/19.3.0.0/admin/po_terms
    $ORACLE_BASE/TESTDB/db/tech_st/temp/TESTDB

    14. Create the Directory Objects

    The TXK utility was then run in createDirObject mode for each required OS path.

    perl $ORACLE_HOME/appsutil/bin/txkCfgUtlfileDir.pl \
    -contextfile=$CONTEXT_FILE \
    -oraclehome=$ORACLE_HOME \
    -outdir=$ORACLE_HOME/appsutil/log \
    -mode=createDirObject

    The utility prompted for:

    Enter the APPS Password:
    Enter the SYSTEM Password:
    Enter the OS path for which directory object needs to be created:

    Each of the required paths completed with:

    Successfully Completed the script
    ERRORCODE = 0 ERRORCODE_END

    15. Run setUtlFileDir

    The next step was:

    perl $ORACLE_HOME/appsutil/bin/txkCfgUtlfileDir.pl \
    -contextfile=$CONTEXT_FILE \
    -oraclehome=$ORACLE_HOME \
    -outdir=$ORACLE_HOME/appsutil/log \
    -mode=setUtlFileDir

    The script returned:

    WARNING: Incorrect value s_applptmp detected on Apps Tier nodes.

    However:

    Successfully Completed the script
    ERRORCODE = 0 ERRORCODE_END

    Therefore the operation itself succeeded, while the s_applptmp issue was recorded as a warning for further review.

    16. Synchronize UTL_FILE_DIR

    The next documented step was:

    perl $ORACLE_HOME/appsutil/bin/txkCfgUtlfileDir.pl \
    -contextfile=$CONTEXT_FILE \
    -oraclehome=$ORACLE_HOME \
    -outdir=$ORACLE_HOME/appsutil/log \
    -mode=syncUtlFileDir \
    -skipautoconfig=yes

    This completed successfully:

    Successfully Completed the script
    ERRORCODE = 0 ERRORCODE_END

    At this point the TXK UTL_FILE_DIR synchronization portion was complete.

    17. Clean EBS Node Configuration

    The next database-side EBS step was:

    EXEC FND_CONC_CLONE.SETUP_CLEAN;
    
    COMMIT;

    Output:

    PL/SQL procedure successfully completed.
    
    Commit complete.

    Immediately afterward, querying FND_NODES returned:

    no rows selected

    This was expected at this point because SETUP_CLEAN removes the existing node registrations so that AutoConfig can rebuild them.

    18. Run Database AutoConfig

    Database AutoConfig was then executed from the database context directory:

    cd $ORACLE_HOME/appsutil
    sh adautocfg.sh

    The script prompted:

    Enter the APPS user password:

    The AutoConfig log was located at:

    $ORACLE_HOME/appsutil/log/TESTDB_dbctx/[timestamp]/adconfig.log

    The important output was:

    Context Value Management will now update the Context file
    
    Updating Context file...COMPLETED
    
    Attempting upload of Context file and templates to database...COMPLETED
    
    Updating rdbms version in Context file to db19
    Updating rdbms type in Context file to 64 bits
    
    Configuring templates from ORACLE_HOME ...
    
    AutoConfig completed successfully.

    This confirmed that the database AutoConfig completed successfully.

    19. Validate EBS Node Registration

    After AutoConfig completed successfully, the final validation was to confirm that the EBS node registrations had been recreated.

    SET LINES 200
    SET PAGES 50
    
    COLUMN node_name FORMAT A15
    COLUMN host_name FORMAT A30
    COLUMN status FORMAT A10
    COLUMN cp FORMAT A5
    COLUMN forms FORMAT A5
    COLUMN web FORMAT A5
    COLUMN admin FORMAT A5
    COLUMN db FORMAT A5
    COLUMN webhost FORMAT A30
    COLUMN last_update FORMAT A20
    
    ALTER SESSION SET NLS_DATE_FORMAT='DD-MON-YYYY HH24:MI:SS';
    
    SELECT node_name,
           host || '.' || domain host_name,
           DECODE(status,'Y','active','inactive') status,
           DECODE(support_cp,'Y','yes','no') cp,
           DECODE(support_forms,'Y','yes','no') forms,
           DECODE(support_web,'Y','yes','no') web,
           DECODE(support_admin,'Y','yes','no') admin,
           DECODE(support_db,'Y','yes','no') db,
           webhost,
           last_update_date
    FROM apps.fnd_nodes
    WHERE node_name != 'AUTHENTICATION'
    ORDER BY 7,1;

    Output:

    NODE_NAME       HOST_NAME                      STATUS     CP    FORMS WEB   ADMIN DB    WEBHOST                   LAST_UPDATE_DATE
    --------------- ------------------------------ ---------- ----- ----- ----- ----- ----- ------------------------- --------------------
    EBSNODE         [masked-hostname]              inactive   no    no    no    no    yes                           18-AUG-2026 12:50:45

    The node registration was successfully rebuilt with the database tier configuration active (DB = yes) and other tiers set to inactive pending app-tier AutoConfig execution.

    Root Cause: Two Independent Issues

    The investigation identified two distinct, independent issues that required separate solutions. This is important to understand because confusing the two would have masked the actual root cause in each case.

    Issue 1 — APPS Account Lockout (Database Authentication Layer)

    The TXK utility reported:

    Invalid APPS database user credentials.

    The detailed validation log showed:

    ORA-28000: The account is locked.

    The APPS account was repeatedly becoming LOCKED(TIMED) due to repeated authentication attempts. SplashBI sessions were identified as contributing to those attempts. This was resolved by stopping the interfering sessions and unlocking the APPS account.

    Issue 2 — Missing AppsUtil JRE (Environment/Configuration Layer)

    During the later TXK workflow, syncUtlFileDir encountered:

    Invalid JDK TOP:
    $ORACLE_HOME/appsutil/jre/bin/java

    The expected JRE path did not exist, while a valid JRE was available under:

    $ORACLE_HOME/appsutil/clone/jre

    This was resolved by copying the JRE to the expected AppsUtil location after taking a backup. This issue was completely independent of the APPS account lockout and would have persisted even if APPS had been unlocked from the start.

    Key lesson: Do not conflate unrelated failures during troubleshooting. Each issue required its own diagnosis and its own fix. Resolving one would not have solved the other.

    Troubleshooting Sequence

    Phase 1: APPS/TXK Troubleshooting

    1. Validate CDB/PDB state
    2. Validate EBS context configuration
    3. Validate TNS connectivity
    4. Check APPS account status
    5. Inspect validate_apps_password.log
    6. Identify repeated APPS authentication attempts
    7. Stop interfering SplashBI sessions
    8. Unlock APPS
    9. Validate direct APPS login

    Phase 2: TXK/UTL_FILE_DIR Configuration

    1. Verify AppsUtil JRE
    2. Take AppsUtil backup
    3. Restore expected appsutil/jre if required
    4. Create directory objects
    5. setUtlFileDir
    6. syncUtlFileDir -skipautoconfig=yes
    7. FND_CONC_CLONE.SETUP_CLEAN
    8. COMMIT
    9. Run Database AutoConfig
    10. Validate FND_NODES
    11. Perform final verification

    Troubleshooting Lessons

    1. Don’t trust the top-level TXK error blindly

    This:

    Invalid APPS database user credentials

    does not necessarily mean the APPS password is wrong.

    Always inspect:

    validate_apps_password.log

    In this case it exposed:

    ORA-28000: The account is locked.

    2. Check for external applications causing APPS lockouts

    If APPS repeatedly becomes:

    LOCKED(TIMED)

    don’t continuously unlock it and retry.

    Look for applications such as:

    • SplashBI
    • integrations
    • monitoring tools
    • application servers
    • scheduled jobs

    that may still have stale APPS credentials.

    3. In a CDB/PDB environment, verify the complete connection path

    Validate:

    • CDB
    • PDB
    • SERVICE_NAME
    • TNS alias
    • PORT
    • APPS account

    For example:

    tnsping TESTDB
    sqlplus apps@TESTDB

    4. Inspect the TXK source when the error is unclear

    The getJavaCmd function made the missing JRE requirement explicit in the source code. This was much more useful than simply treating the Invalid JDK TOP error as a generic Java problem.

    5. Take a backup before modifying AppsUtil

    The backup provided a simple rollback point:

    tar -czf $ORACLE_BASE/TESTDB/appsutil_pre_jre_fix_$(date +%Y%m%d_%H%M%S).tar.gz \
        TESTDB_dbctx.xml \
        txkSetCfgCDB.env \
        clone/jre

    Final Validation

    With AutoConfig completed successfully, the final validations confirmed that the database-side configuration was healthy and that the EBS node registration had been rebuilt:

    • FND_NODES query returned the EBSNODE with DB support active
    • APPS login successful
    • AutoConfig completed with no errors
    • txkCfgUtlfileDir.pl returned ERRORCODE = 0 for all modes
    • UTL_FILE_DIR directory objects successfully created

    The database-tier configuration was now ready for app-tier AutoConfig to follow, which would complete the clone or refresh procedure.

    Closing

    This issue initially looked like a simple APPS authentication problem. The actual troubleshooting required following the failure through several layers: APPS account state → PDB/service connectivity → authentication log inspection → identification of external authentication interference → TXK environment validation → prerequisite verification → directory object creation → EBS node cleanup → AutoConfig.

    The key was to validate each layer independently and avoid assuming causality between separate findings. The APPS lockout issue and the missing JRE issue were distinct problems requiring distinct solutions.

    ما شاء الله

  • When OPMN Enters a Restart Loop: Troubleshooting Oracle EBS OC4J Instability on AIX

    How I Tracked Down a Crash Sequence Across EBS Application Logs, OPMN, and AIX OS Events

    Alhamdulillah.

    Around 3 AM, the production EBS application tier was in an unstable state. The application tier was experiencing repeated process crashes and restart failures, and the OPMN logs showed multiple processes entering a recovery cycle.

    I start scrolling through logs and find the kind of chaos that makes troubleshooting interesting: OACORE processes that won’t stay alive, Forms instances crashing in sequence, OPMN repeatedly detecting failed JVM processes and attempting to restart them.

    This is the story of how a single incident revealed the importance of timeline building, log correlation, and knowing the difference between recovery and root cause.

    The Environment

    • Oracle E-Business Suite R12.1
    • AIX 7.1
    • OC4J 10.1.3 with OPMN management
    • IBM Java 7
    • Multiple OACORE and Forms processes

    The First Red Flag

    I start with the OACORE process startup logs. The timeline shows a clean start:

    20:57 — OACORE Startup
    26/08/12 20:57:06 Start process

    But three seconds later, something unexpected appears. The JVM is configured with 2560 MB heap and Gencon garbage collection. Then:

    26/08/12 20:57:09 WARNING: GC: Unable to access pending References.
    26/08/12 20:57:09 WARNING: GC: Unable to access pending Reference lock.

    That’s interesting, but not necessarily fatal. GC warnings can be transient. I keep digging.

    In the same timeframe, OACORE logs show an RMI failure:

    Error: Could not connect to the remote server
    caused by: oracle.oc4j.security.KeyExchange.getSecretKey
    caused by: Unsupported secret key algorithm: DES

    This is worth noting. An unsupported DES algorithm error in the OC4J RMI layer. But here’s the thing: I have two observations now, and I don’t yet know if they’re related, or if either one caused what happens next.

    The Cascade

    Fast forward to 01:26 AM on August 13. The OPMN logs suddenly light up:

    01:26 — Multiple Forms Processes Crash
    26/08/13 01:26:07 Process Crashed: default_group~forms~default_group~2
    26/08/13 01:26:07 Process Crashed: default_group~forms~default_group~5
    26/08/13 01:26:07 Process Crashed: default_group~forms~default_group~7
    26/08/13 01:26:07 Process Crashed: default_group~forms~default_group~8
    26/08/13 01:26:07 Process Crashed: default_group~forms-c4ws~default_group~1

    Not just one. Not even two. Five processes are crashing in the same second. The simultaneous failure of multiple Forms-related processes suggested that the issue was broader than a single application process failure.

    OPMN immediately begins detecting these failures. It does what it’s designed to do: attempt to restart failed processes. But the restart attempts are not succeeding cleanly:

    Process Start Error: default_group~forms~default_group~4
    Failed to construct stop command
    Forcefully Terminating Process
    Could not connect to socket while pinging an OC4J process
    Operation: internal (Connection refused)

    OPMN detects the failed processes and repeatedly attempts to restart them, but several restart attempts encounter process communication, socket, and port-information errors. This is the kind of situation where the logs quickly become a wall of repeated error messages as OPMN continues attempting to recover the affected processes.

    The Port Information Mystery

    As I dig deeper into the OPMN logs, I find another clue that doesn’t quite fit the picture yet:

    Host and Port information for port type jms not sent by the OC4J process
    WARNING: OC4J did not send the protocol value for port id: jms

    OPMN relies on port information to manage processes. OPMN was not receiving the expected JMS port/protocol information from the OC4J process, which complicated process management and restart attempts.

    At this point I have:

    • GC warnings on OACORE startup
    • An RMI/DES algorithm error
    • Multiple OC4J processes crashing simultaneously
    • OPMN unable to restart them cleanly
    • Missing JMS port/protocol information

    But which one caused the others? That’s when I make an important decision: instead of trying to decide, I document what I observe and build a timeline.

    Build a Timeline, Not a Theory

    When several application processes fail around the same period, it is tempting to take the first recognizable error and declare it the root cause. In this incident, the OACORE GC warnings and OC4J DES error were early clues, but the subsequent OPMN logs showed that the failure was broader.

    At this point, I have a comprehensive picture of application failures. But I’ve been told the server was rebooted. So I check the AIX logs.

    Incident Timeline

    Time Observation
    Aug 12, 20:57 OACORE processes started
    20:57:09 IBM JVM GC warnings observed (Unable to access pending References)
    20:57 OC4J RMI connection failure — Unsupported secret key algorithm: DES
    Aug 13, 01:26:07 Multiple Forms / Forms-C4WS processes crashed simultaneously
    01:28 onward OPMN restart attempts encountered process start, socket, and port-information errors
    02:36 AIX system shutdown recorded; system subsequently rebooted
    After reboot Application services recovered and remained stable

    The OS Logs Tell a Different Story

    Now I verify the OS timeline. First, boot information:

    who -b

    This tells me the boot time was Aug 13 at 02:36. Next I check the AIX error log with a time-window query around the known failure period:

    errpt -s 0813010026 -e 0813030026

    This returns:

    2BFA76F6   0813023626 T S SYSPROC
    SYSTEM SHUTDOWN BY USER

    Confirmed: SYSTEM SHUTDOWN BY USER at 02:36.

    02:36 — AIX Reboot
    System boot time: Aug 13 02:36

    After the reboot, the application services came back up and remained stable.

    What the Logs Proved — and What They Didn’t

    Proved by the evidence:

    • Multiple EBS application processes crashed
    • OPMN attempted recovery and restarts
    • Several restart attempts encountered communication and port-related errors
    • AIX recorded a system shutdown by user at 02:36
    • who -b confirmed the system boot time as 02:36
    • Application services recovered after the reboot

    Not proved by the evidence:

    • That the GC warnings caused the incident
    • That the DES error caused the process crashes
    • That the JMS port issue was the initiating failure
    • That the reboot fixed the underlying root cause
    • The exact initiating cause of the overall process-crash cascade

    The Critical Distinction

    Here’s where I have to be careful about my conclusions.

    The reboot restored service. That’s a fact. But did the reboot fix the root cause, or did it just clear transient state? These are two different things.

    A reboot can:

    • Clear unstable JVM runtime state
    • Terminate stuck processes
    • Clear transient socket/process communication state
    • Reinitialize application-tier processes and their associated listeners/ports

    None of those tell me what originally triggered the failure.

    What I can confirm:

    • OACORE: Startup with GC warnings
    • OC4J RMI: DES algorithm error
    • Forms and Forms-C4WS: Multiple process crashes during the recovery/restart sequence
    • OPMN: Multiple restart attempts encountered process communication failures, connection refusals, and missing JMS port/protocol information
    • Recovery: Reboot restored stability

    What I cannot conclusively determine from logs: the exact initiating cause of the cascade.

    When to Raise an Oracle Support SR

    In production, your first priority is recovery. But recovery should not be your last action.

    If you encounter OC4J/OPMN instability involving multiple process crashes and restart failures, here’s the right approach:

    Before you reboot:
    • If service impact is acceptable, preserve application logs, OPMN logs, and OS error logs (errpt)
    • Consider opening an SR with Oracle Support before recovery actions
    • Provide an SR reference for all follow-up communication

    If service impact demands immediate recovery:
    • Proceed with the reboot
    • Collect and preserve log artifacts afterward
    • Raise the SR as soon as practical with the preserved log artifacts
    • Include full OPMN, OACORE, Forms logs and AIX errpt output

    An SR with Oracle Support positions your incident for definitive analysis. It moves you from “we rebooted and it worked” to “here’s what happened, and here’s what Oracle Support says about it.”

    The Troubleshooting Checklist

    When you see OC4J instability, use these commands to build your timeline:

    Grep for key patterns across logs:

    grep -n -E "GC: Unable|Unsupported secret|Process Start Error|Process Crashed|Connection refused|Exception|ERROR|FATAL" <oacore_log>

    Check OPMN for the restart cycle:

    grep -n -E "Process Crashed|Process Start Error|Connection refused|Port information" <opmn.log>

    Verify boot timeline and OS events:

    who -b && uptime && last reboot | head -5

    Extract the error events in a time window (the most useful approach):

    errpt -s 0813010026 -e 0813030026   # Adjust dates for your incident

    This time-window query is much more powerful than a simple grep because it shows you events in context, within the period your incident occurred.

    What I Learned

    Alhamdulillah for this incident, because it reinforced several critical principles:

    The first error is not the root cause. I started with GC warnings and an RMI error. But the real story emerged from the sequence of crashes, not from the opening symptoms.

    Build a timeline, not a theory. A timeline shows sequence and correlation; theories can introduce assumptions. Timestamps tied together the OACORE observations, the Forms crashes, OPMN’s restart failures, and ultimately the reboot recovery.

    Recovery and RCA are different questions. The reboot answered “what restored service?” but not “what caused the failure?” It’s important to say the difference out loud.

    Correlate application and OS logs. OPMN logs showed application behavior; AIX errpt showed OS timeline. Together they tell the full story.

    Open an SR when multiple OC4J components fail. When you have a cascade of crashes involving Forms, OACORE, and OPMN restart failures, that’s beyond the scope of “try restarting the services.” Engage Oracle Support early with an SR. It positions you for definitive analysis and creates a record for future reference.

    Closing Thoughts

    This incident reinforced something I’ve learned through years of production troubleshooting in EBS and Oracle Database: patience with logs pays off.

    The reboot restored application stability, but the available evidence did not establish whether the reboot cleared a transient runtime condition or whether the underlying initiating problem had already resolved.

    Instead of manufacturing an RCA that the logs don’t support, I documented: application symptoms → OPMN behavior → OS timeline → reboot → recovery → RCA limitation.

    For any DBA handling production incidents, this is the framework:

    1. Document what the evidence proves
    2. Clearly identify what it does not prove
    3. Continue the investigation with Oracle Support if RCA is incomplete

    ما شاء الله

  • Oracle EBS R12.2 Multi-Node Patching in Downtime Mode: Recovering a Failed Slave Node with restart=yes

    Alhamdulillah, sharing another real patching session — this one from an Oracle E-Business Suite R12.2 multi-node environment. While applying a patch in downtime mode, I encountered an interesting situation: the patch completed successfully on the master node but failed on the slave node, while the final ADOP output still showed:

    adop exiting with status = 0 (Success)

    This post walks through the complete sequence: pre-checks, the slave-node failure, investigation, recovery using restart=yes, and the SQL checks used to verify the final patch state.

    Environment

    • Oracle E-Business Suite R12.2.13
    • AD/TXK Delta 15 (ADOP C.Delta.15)
    • Two application nodes: appsnode1 (master/admin node) and webnode1 (slave/web node)
    • Oracle Database 19c on dbnode1
    • Patch mode: downtime (single file system apply; no online patching cutover involved)

    Pre-Checks Before the Apply

    Before starting the patch, I checked three things: the current ADOP session status, application processes on all application nodes, and the current file-system edition.

    From the master node:

    [applmgr@appsnode1]$ adop -status
    Node Name Node Type Phase Status
    --------------- ---------- --------------- ---------------
    appsnode1 master APPLY ACTIVE
    CLEANUP NOT STARTED
    webnode1 slave APPLY ACTIVE
    CLEANUP NOT STARTED

    I also confirmed I was operating from the run edition:

    [applmgr@appsnode1]$ echo $FILE_EDITION
    run

    Then I checked for application-tier processes:

    [applmgr@appsnode1]$ ps -ef | egrep 'FNDLIBR|FNDSM|FNDCRM|FNDOPP|oacore|forms|WebLogic|NodeManager' | grep -v grep

    There was no output, confirming the expected services were down on the master node.

    Important: In a multi-node environment, repeat these process checks on every application node. Checking only the master node is not sufficient — a condition on a slave node can otherwise go unnoticed until the apply reaches it.

    Starting the Downtime Apply

    The downtime apply was initiated from the master node:

    [applmgr@appsnode1]$ adop phase=apply apply_mode=downtime patches=<patch_number>

    ADOP manages execution across the registered application-tier nodes, so there was no need to manually start ADOP on the slave node.

    During initialization, ETCC reported missing database fixes:

    [WARNING] ETCC: The following required database fixes have not been applied to node dbnode1:
    ...
    Refer to My Oracle Support Knowledge Document 1594274.1 for instructions.

    ETCC warnings should always be reviewed before proceeding. In this case, the apply was allowed to continue based on the maintenance plan, while the outstanding database fixes were documented and tracked separately for a subsequent maintenance activity.

    Master Succeeded, Slave Failed

    The patch application succeeded on the master node but failed on the slave:

    Applying <patch> patch(es) on admin node: [appsnode1].
    txkADOPEvalSrvStatus.pl returned SUCCESS
    Applying <patch> patch(es) on node(s): [webnode1].
    Running in Serial
    [ERROR] adop phase=apply failed on Node: "webnode1"
    Summary report for current adop session:
    Node webnode1: Failed
    - Apply status: Failed
    Node appsnode1: Completed successfully
    - Apply status: Completed successfully
    adop exiting with status = 0 (Success)

    This is an important operational lesson. Despite the slave-node failure being clearly reported in the summary, the ADOP invocation ended with adop exiting with status = 0 (Success).

    Therefore, relying only on the shell return code or the final ADOP exit line is not sufficient for determining whether every application node completed successfully. The per-node summary must also be reviewed.

    I confirmed the state with:

    adop -status -detail

    which showed:

    Node Name Node Type Phase Status
    --------------- ---------- --------------- ---------------
    appsnode1 master APPLY ACTIVE
    webnode1 slave APPLY FAILED

    For automated patching this matters even more: scripts should validate the ADOP session and per-node phase status rather than treating $? = 0 alone as proof of a successful multi-node patch.

    Investigating the Slave Node

    I then logged into webnode1. Application services were expected to be completely down, so I checked for NodeManager:

    [applmgr@webnode1]$ ps -ef | grep -i nodemanager | grep -v grep
    applmgr 4148261 1 0 Jun18 ... weblogic.NodeManager -v

    A NodeManager process was still running, and had been since a previous month.

    I checked whether other WebLogic or EBS application processes were present:

    [applmgr@webnode1]$ ps -ef | grep -i weblogic | grep -v grep

    Only the NodeManager process remained. Concurrent Manager processes were also absent:

    [applmgr@webnode1]$ ps -ef | grep FNDLIBR | grep -v grep

    No output was returned.

    Since the environment was in a planned patching outage and no managed servers were running, the stale NodeManager process was stopped:

    [applmgr@webnode1]$ kill 4148261

    I then verified again:

    [applmgr@webnode1]$ ps -ef | egrep 'FNDLIBR|FNDSM|oacore|forms|WebLogic|NodeManager' | grep -v grep

    No relevant processes remained.

    A Note About Root Cause

    The presence of the stale NodeManager was an abnormal condition that needed to be corrected before retrying the patch. However, unless the ADOP logs explicitly identify that process as the reason for the failure, it is safer not to state that NodeManager was definitively the root cause.

    What can be established from this incident is:

    1. The slave-node apply failed.
    2. A stale NodeManager process was discovered on that node.
    3. The process was stopped after confirming application services were intended to be down.
    4. The failed ADOP apply subsequently completed successfully with restart=yes.

    For a formal RCA, the relevant ADOP and slave-node logs should be used to establish the exact failure mechanism.

    Also, before terminating any process, verify that:

    • It belongs to the expected EBS environment/file system.
    • No managed servers depend on it.
    • Application services are intended to be down.
    • The action complies with your production change procedure.

    Recovering the Failed Apply with restart=yes

    After cleaning up the slave node, I returned to the master node. The existing failed apply was resumed using:

    [applmgr@appsnode1]$ adop phase=apply patches=<patch_number> apply_mode=downtime restart=yes

    ADOP detected the existing session:

    Checking for existing adop sessions.
    Application tier services are down.
    Continuing with the existing session [Session ID: <n>].

    The apply was then processed successfully:

    Applying <patch> patch(es) on admin node: [appsnode1].
    txkADOPEvalSrvStatus.pl returned SUCCESS
    Applying <patch> patch(es) on node(s): [webnode1].
    Running in Serial
    txkADOPEvalSrvStatus.pl returned SUCCESS

    The final summary showed both nodes successful:

    Summary report for current adop session:
    Node webnode1: Completed successfully
    - Apply status: Completed successfully
    Node appsnode1: Completed successfully
    - Apply status: Completed successfully

    The important point here is that I resumed the existing ADOP session rather than initiating an unrelated fresh patching attempt. The recovery command was executed from the master/admin node, allowing ADOP to coordinate processing on the registered slave node.

    For the exact semantics of restart=yes in downtime mode and multi-node processing, refer to the Oracle E-Business Suite Patching Guide and the relevant My Oracle Support documentation. What I can report from this incident is the observed behaviour above: ADOP continued the existing session and both nodes subsequently reported success.

    Post-Restart Validation

    After the successful rerun, I checked the detailed ADOP status:

    [applmgr@appsnode1]$ adop -status -detail

    The nodes no longer showed a failed apply state.

    I also scanned the latest ADOP logs:

    [applmgr@appsnode1]$ adopscanlog -latest=yes
    Scanning .../log/adop/<session>/ directory ...
    No Errors.

    adopscanlog is a useful quick check after an ADOP phase because it searches the session logs for reported errors that may otherwise be easy to miss.

    SQL Verification: Confirming Patch Registration

    I don’t rely only on the ADOP console output. After patching, I also verify the database-side patch records.

    1. Check AD_BUGS

    SET LINES 200
    COL BUG_NUMBER FORMAT A15
    COL CREATION_DATE FORMAT A22
    SELECT bug_number,
    creation_date
    FROM ad_bugs
    WHERE bug_number = '<patch_number>';

    This confirms whether the specified bug/patch number is recorded in AD_BUGS. Because patch contents and registration behaviour can vary, this check should be used together with ADOP status, logs, and the relevant applied-patch records rather than as standalone proof of complete patch application. Running the same query before patching is also useful, because it establishes a clear before-and-after state.

    2. Check Applied Patch Records

    SELECT applied_patch_id,
    patch_name,
    patch_type,
    source_code,
    creation_date,
    last_update_date,
    data_model_done_flag
    FROM ad_applied_patches
    WHERE patch_name = '<patch_number>';

    Depending on the patch structure, this helps confirm the corresponding patch-driver records.

    3. Verify AD/TXK Code Levels

    COL ABBREVIATION FORMAT A15
    COL CODELEVEL FORMAT A25
    SELECT abbreviation,
    codelevel
    FROM ad_trackable_entities
    WHERE abbreviation IN ('ad','txk')
    ORDER BY abbreviation;

    For this environment the query returned:

    ABBREVIATION CODELEVEL
    --------------- -------------------------
    ad C.15
    txk C.15

    This check is especially important when applying AD/TXK-related patches or preparing for an EBS Release Update Pack.

    4. Verify the EBS Release

    SELECT release_name
    FROM fnd_product_groups;

    Expected for this environment: 12.2.13.

    5. Verify Node Registration

    COL NODE_NAME FORMAT A25
    SELECT node_name,
    support_cp,
    support_forms,
    support_web,
    support_admin
    FROM fnd_nodes
    ORDER BY node_name;

    This provides a quick sanity check of application-tier node registration and responsibilities.

    6. Verify ADOP Valid Nodes

    SELECT *
    FROM adop_valid_nodes
    ORDER BY node_name;

    Both application nodes should appear as expected.

    In this case, the final checks confirmed:

    • The bug/patch number was recorded in AD_BUGS.
    • The corresponding applied-patch records were present.
    • AD and TXK were at the expected C.15 level.
    • EBS remained at Release 12.2.13.
    • Both application nodes were properly registered in FND_NODES.
    • Both nodes appeared correctly in ADOP_VALID_NODES.
    • adopscanlog reported no errors after the successful rerun.

    Together, these checks provide much stronger verification than relying on the final ADOP console message alone.

    Complete the Session with Cleanup

    After confirming the apply completed successfully, the session still showed:

    CLEANUP NOT STARTED

    The cleanup phase was then run:

    adop phase=cleanup

    After cleanup, perform another status and log review to make sure the session is in the expected final state.

    Key Takeaways

    Don’t rely only on ADOP’s exit status in a multi-node environment. In this incident, ADOP printed status = 0 (Success) even though the per-node summary clearly showed the slave node had failed.

    Check every application node before a downtime apply. A clean process list on the master does not guarantee the slave nodes are equally clean.

    Use the per-node ADOP status as part of your success criteriaadop -status -detail.

    Investigate the failed node before retrying. In this case a stale NodeManager process was discovered and removed before the successful retry. Unless supported by the logs, however, avoid treating correlation as a confirmed root cause.

    Resume the existing failed apply appropriately — after correcting the condition on the slave node, the existing apply was resumed from the master node with restart=yes.

    Use adopscanlog -latest=yes after the rerun.

    Verify patch registration in the databaseAD_BUGS, AD_APPLIED_PATCHES, AD_TRACKABLE_ENTITIES, FND_NODES, ADOP_VALID_NODES, FND_PRODUCT_GROUPS.

    Run ETCC and review its findings. If required database fixes are deferred, document and track them for the appropriate maintenance window rather than allowing them to become forgotten technical debt.

    Complete the ADOP cleanup phaseadop phase=cleanup.

    Final Thought

    Multi-node EBS patching adds a layer of validation that is easy to overlook: success on the master node does not automatically mean success across the application tier.

    The most useful lesson from this incident was not the retry command itself, but the validation process around it:

    Check every node → read the ADOP per-node summary → investigate the failed node → resume the existing session → scan the logs → verify the database records → complete cleanup.

    That sequence gives you a far more defensible and auditable patching result than relying on a single Success message at the end of the command.

    I hope this helps anyone troubleshooting a failed application-tier node during Oracle E-Business Suite R12.2 downtime patching. Feel free to leave a comment if you have questions.


    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.

  • Oracle EBS R12.2 AutoConfig Template Version Conflict After Patching: How to Preserve Customizations

    Alhamdulillah, sharing another real troubleshooting session — this one from an Oracle E-Business Suite R12.2 multi-node environment. After applying a patch, AutoConfig aborted on the primary application node with a template version conflict. This post walks through the exact error, how I identified which template entries were genuine customizations versus Oracle-shipped content, and the correct way to rebase a custom template so AutoConfig completes cleanly.

    Environment

    • Oracle E-Business Suite R12.2.13, AD/TXK Delta 15
    • Two application nodes: appsnode1 (primary/admin) and webnode1 (secondary web node)
    • Database: Oracle 19c on dbnode1
    • Third-party customization: More4Apps servlet registered in oacore web.xml

    The Error

    After patching, running AutoConfig on the run file system failed immediately:

    [applmgr@appsnode1 scripts]$ sh adautocfg.sh
    Enter the APPS user password:
    The log file for this session is located at: .../admin/log/<timestamp>/adconfig.log
    wlsDomainName: EBS_domain
    WLS Domain Name is VALID.
    AutoConfig is configuring the Applications environment...
    AutoConfig will consider the custom templates if present.
    Version Conflicts among development maintained and customized templates encountered; aborting AutoConfig run.

    The log pinpointed the conflicting template:

    [ FND_TOP ]
    
    [ VERSION CONFLICTS INFORMATION ]
    Template shipped by oracle is having a version different than the template lying in custom directory.
    Template shipped by Oracle : $FND_TOP/admin/template/oacore_web_xml_FMW.tmp (version: 120.17.12020000.33)
    Custom template            : $FND_TOP/admin/template/custom/oacore_web_xml_FMW.tmp (version: 120.17.12020000.31)
    Please resolve the differences between the two templates or refer to the Oracle E-Business Suite Setup Guide for further details.

    What happened is straightforward: the patch delivered a newer version of oacore_web_xml_FMW.tmp (120.17.12020000.33), but a custom copy of the older version (120.17.12020000.31) exists under $FND_TOP/admin/template/custom. AutoConfig detects the mismatch and refuses to run, because blindly using the stale custom template would silently discard whatever Oracle changed in the new version.

    Root Cause Analysis: What Is Actually a Customization?

    This is the critical analysis step. Before touching anything, you need to answer one question precisely: which lines in the custom template are genuine customizations, and which are just old Oracle-shipped content?

    First, take a backup and diff the new shipped template against the custom one:

    cd $FND_TOP/admin/template
    
    cp -p custom/oacore_web_xml_FMW.tmp \
          custom/oacore_web_xml_FMW.tmp.pre_patch_25Jul2026
    
    diff -u oacore_web_xml_FMW.tmp custom/oacore_web_xml_FMW.tmp

    The diff showed three differences beyond the header:

    1. A m4aServlet servlet and servlet-mapping (More4Apps) present only in the custom template
    2. A SyncServlet servlet and servlet-mapping present only in the custom template
    3. A RequestAuditReport (QP) servlet present only in the new Oracle template

    At first glance it looks like there are two customizations to carry forward: m4aServlet and SyncServlet. This is where a wrong assumption would corrupt the template. To verify, I compared the old shipped template against the new shipped template. In an R12.2 dual file system this is easy — the other file system still had the previous patch level, so both shipped versions were available:

    diff -u \
      <old_fs>/EBSapps/appl/fnd/12.0.0/admin/template/oacore_web_xml_FMW.tmp \
      <new_fs>/EBSapps/appl/fnd/12.0.0/admin/template/oacore_web_xml_FMW.tmp

    Key excerpt from the shipped-vs-shipped diff:

    -   <servlet>
    -     <servlet-name>SyncServlet</servlet-name>
    -     <servlet-class>oracle.apps.jtf.cac.sync.transport.SyncServlet</servlet-class>
    -  </servlet>
    ...
    +     <servlet>
    +         <servlet-name>RequestAuditReport</servlet-name>
    +         <servlet-class>oracle.apps.qp.servlet.RequestAuditReport</servlet-class>
    +    </servlet>

    This settled the question:

    • SyncServlet was Oracle-shipped content in version .31 and is no longer present in the .33 shipped template. It was never a customer customization, so it should NOT be carried forward.
    • RequestAuditReport is new Oracle-shipped content in .33 — it must be retained.
    • m4aServlet was not present in either shipped template version compared — it exists only in the custom directory, which identifies it as the genuine site-specific customization.

    If I had merged the old custom template on top of the new one, I would have reintroduced a servlet that is no longer present in Oracle’s newer shipped template. This is why “just copy your custom entries into the new template” advice found on many forums is dangerous — verify against the shipped versions first.

    The Fix: Rebase the Custom Template

    The correct approach: replace the custom template with the new shipped version, then re-apply only the genuine customization.

    cd $FND_TOP/admin/template
    
    # Rebase custom template on the new shipped version
    cp -p oacore_web_xml_FMW.tmp custom/oacore_web_xml_FMW.tmp
    
    # Re-insert the only genuine customization (m4aServlet)
    sed -i '/<!-- FND Servlets -->/a\
    \
      <servlet>\
        <servlet-name>m4aServlet</servlet-name>\
        <servlet-class>com.more4apps.r12.servlet.XmlServlet</servlet-class>\
      </servlet>\
    \
      <servlet-mapping>\
        <servlet-name>m4aServlet</servlet-name>\
        <url-pattern>/m4aServlet/*</url-pattern>\
      </servlet-mapping>\
    ' custom/oacore_web_xml_FMW.tmp

    This matches the procedure in the Oracle E-Business Suite Setup Guide: when a patch delivers a newer version of a template you have customized, copy the new Oracle template into the custom directory and reapply the still-required customizations to that copy — never the other way around.

    Verify before rerunning AutoConfig — the headers must match, and the only diff must be the customization:

    grep '\$Header' oacore_web_xml_FMW.tmp custom/oacore_web_xml_FMW.tmp
    
    oacore_web_xml_FMW.tmp:        version 120.17.12020000.33
    custom/oacore_web_xml_FMW.tmp: version 120.17.12020000.33
    
    diff -u oacore_web_xml_FMW.tmp custom/oacore_web_xml_FMW.tmp
    --- only the m4aServlet servlet + mapping block should appear ---

    As an additional best-practice validation, Oracle recommends running the AutoConfig configuration-check utility to preview the impact of template changes before the actual AutoConfig run:

    $AD_TOP/bin/adchkcfg.sh contextfile=$CONTEXT_FILE

    Rerun AutoConfig:

    cd $ADMIN_SCRIPTS_HOME
    sh adautocfg.sh
    
    ...
    Configuring templates from all of the product tops...
            Configuring AD_TOP........COMPLETED
            Configuring FND_TOP.......COMPLETED
            ...
    AutoConfig completed successfully.

    A Bonus Finding on the Second Node

    While repeating the check on the secondary web node, the header comparison came back clean — custom template already at .33 — but something was off:

    grep -n 'm4aServlet' $FND_TOP/admin/template/custom/oacore_web_xml_FMW.tmp
    (no output)
    
    grep -n 'm4aServlet' $FND_TOP/admin/template/custom/oacore_web_xml_FMW.tmp_ORIG
    61:    <servlet-name>m4aServlet</servlet-name>

    The current custom template matched the newer shipped version, but the m4aServlet customization was missing. Interestingly, the _ORIG backup still contained the customization. This indicated that during an earlier template update, the customization was not carried forward. AutoConfig had been running fine and web.xml was being generated — just without the third-party servlet.

    I re-inserted the m4aServlet block using the same sed command, verified the diff, and ran AutoConfig on that node as well:

    AutoConfig completed successfully.

    This is the real lesson of the post. A template version conflict at least fails loudly. A customization that is accidentally omitted during a template rebase may not — AutoConfig can complete successfully while the generated configuration no longer contains the required customization, and you find out later when the third-party tool stops working after a bounce.

    An Additional Message Observed

    The AutoConfig log in this run also contained:

    ECC not enabled, setting FND_ECC_ENABLED to FALSE
    [ FND_ECC_ENABLED ]
    INFO : Error updating/creating profile option value.

    In this case Enterprise Command Center is not configured, AutoConfig continued past this message, and the run ultimately exited with status 0. Always evaluate such messages in the context of your own environment rather than assuming every INFO : Error entry can be ignored.

    Post-Fix Checklist

    1. Confirm the customization landed in the generated web.xml (get the target path from the adconfig log and grep for your servlet).
    2. Bounce the application services (at least the oacore managed servers) so the new web.xml is deployed.
    3. Test the customization end to end — for More4Apps, hit the servlet URL from the wizard.
    4. If your patching cycle requires it, regenerate appsutil.zip (perl $AD_TOP/bin/admkappsutil.pl) and refresh the database tier.
    5. Repeat the custom template verification on every application node — as seen above, nodes can drift.

    Key Takeaways

    • Never resolve this error by copying the old custom template over the new shipped one, and never blindly merge either. The Oracle-documented procedure is to copy the new shipped template into the custom directory and re-apply only verified customizations to that copy.
    • Use the dual file system to your advantage: diff old-shipped vs new-shipped to separate Oracle’s changes from your customizations.
    • After any template rebase, diff the shipped and custom templates — the output should contain nothing but your customizations.
    • Audit custom templates on all nodes periodically. A missing customization may not trigger an AutoConfig error, allowing the generated configuration to differ from what you expect.

    I hope this helps someone facing the same conflict. Feel free to leave a comment if you have questions.


    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.

  • EBS 12.2 Rapid Clone dbTechStack Fails with RC-50013 — INS-08101 supportedOSCheck on Oracle Linux 8

    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:

    grep -i CV_ASSUME_DISTID $ORACLE_HOME/cv/admin/cvu_config

    Post-Clone Notes

    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.

  • ORA-00060 Deadlock Forensics in Oracle EBS: When a New APEX Scheduler Job Meets Workflow

    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

    1. 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.
    2. Redeployed the package during an approved change window.
    3. Rescheduled the APEX job to avoid peak Workflow Background Process cycles as an additional safety margin.
    4. 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.

  • Oracle EBS 12.2.4 Upgrade to Oracle Database 19c: Oracle Restart Upgrade, Non-CDB to PDB Conversion, and Lessons Learned

    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

    ComponentDetail
    PlatformSingle standalone Linux host (dbhost) running Oracle Restart / standalone HASnot full RAC Clusterware
    Apps tierSeparate node (appshost) under the applmgr-style owner
    EBS release12.2.4
    AD levelAD.C.Delta.13
    TXK levelTXK.C.Delta.13
    Source GI12.1.0.2
    Target GI19c (19.30)
    Source DBNon-CDB EBS database, SID ebsdb, on 12.1.0.2
    Target DB19c (19.30), ebsdb plugged in as a lowercase PDB inside CDB EBSCDB
    StorageASM diskgroups +DATA and +RECO
    Old GI home/u01/app/12.1.0.2/grid
    New GI home/u01/app/19.0.0/grid
    Old DB home/u01/app/oracle/product/12.1.0.2/dbhome_1
    New DB home/u01/app/oracle/product/19.0.0/dbhome_1

    The overall sequence was four phases:

    1. Grid Infrastructure 12.1.0.2 → 19c (Oracle Restart upgrade)
    2. Database 12.1.0.2 → 19c via DBUA (-keepEvents)
    3. Non-CDB → PDB conversion (plug ebsdb into EBSCDB)
    4. EBS application-tier cutover (AutoConfig, adop)

    The transformation at a glance

            BEFORE                                AFTER
    
        EBS Apps Tier                         EBS Apps Tier
             |                                     |
    12.1.0.2 Non-CDB (ebsdb)        ===>      PDB ebsdb
             |                                     |
    Oracle Restart 12.1.0.2                  CDB EBSCDB (19c)
             |                                     |
    ASM (+DATA / +RECO)                      Oracle Restart 19c
                                                   |
                                             ASM (+DATA / +RECO)

    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)

    PhaseTypical duration
    GI (Oracle Restart) upgrade30–60 min
    DBUA database upgrade2–4 hrs
    Non-CDB → PDB conversion~15 min
    AutoConfig & validation30–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:

    grep -iE "old_crs_home|asm_upgrade|oracle_home" /u01/app/19.0.0/grid/crs/install/crsconfig_params
    OLD_CRS_HOME=/u01/app/12.1.0.2/grid
    ASM_UPGRADE=true

    Then recreated the missing init.cssd pointing at the old home:

    cat > /etc/init.d/init.cssd << 'EOF'
    #!/bin/bash
    ORA_CRS_HOME=/u01/app/12.1.0.2/grid
    export ORA_CRS_HOME
    EOF
    chmod 755 /etc/init.d/init.cssd

    The second wall: a missing OLR pointer

    With init.cssd back, the next thing the script needs is the Oracle Local Registry pointer file, /etc/oracle/olr.loc — also missing.

    A common trap here is to assume the OLR is named <hostname>.olr. It was not. The actual file was local.ocr:

    ls -lrt /u01/app/12.1.0.2/grid/cdata/localhost/
    -rw-r----- 1 oracle dba 503484416 Feb 10 2017 local.ocr

    Always verify OLR integrity before trusting it:

    /u01/app/12.1.0.2/grid/bin/ocrcheck -local
    Device/File integrity check succeeded
    Local registry integrity check succeeded
    Logical corruption check succeeded

    Then recreate the pointer with the correct path:

    mkdir -p /etc/oracle
    cat > /etc/oracle/olr.loc << 'EOF'
    olrconfig_loc=/u01/app/12.1.0.2/grid/cdata/localhost/local.ocr
    crs_home=/u01/app/12.1.0.2/grid
    EOF
    chmod 644 /etc/oracle/olr.loc

    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.

    Deconfigure the broken old stack:

    cd /u01/app/12.1.0.2/grid/crs/install
    perl roothas.pl -deconfig -force
    CLSRSC-337: Successfully deconfigured Oracle Restart stack

    Set the environment correctly (the LD_LIBRARY_PATH is what fixes the CRS-4652 library failure):

    export ORACLE_HOME=/u01/app/12.1.0.2/grid
    export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/lib:/usr/lib

    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:

    rm -f /u01/app/19.0.0/grid/checkpoints/ROOTHAS_STACK
    rm -f /u01/app/19.0.0/grid/checkpoints/ROOTCRS_STACK

    Now re-run the 19c upgrade:

    /u01/app/19.0.0/grid/rootupgrade.sh

    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>
    # as oracle
    xauth add dbhost/unix:11 MIT-MAGIC-COOKIE-1 <cookie>
    export DISPLAY=localhost:11.0
    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

    ScreenChoice
    CDB / PDB conversionLeave 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 objectsEnabled (parallel)
    Upgrade timezone dataEnabled
    compatibleLeave at 12.1.0.2 during the upgrade — preserves rollback. Raise it later, deliberately.
    Listener migrationKeep 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).

    Step 1 — Pre-creation tasks

    perl $AD_TOP/patch/115/bin/txkOnPremPrePDBCreationTasks.pl

    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.

    Step 2 — Compatibility check

    perl $AD_TOP/patch/115/bin/txkChkPDBCompatability.pl

    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. ما شاء الله.

    Step 4 — Post-creation tasks (the messy part)

    perl $AD_TOP/patch/115/bin/txkPostPDBCreationTasks.pl

    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:

    orapwd file='+DATA/EBSCDB/orapwEBSCDB' dbuniquename='EBSCDB' format=12
    srvctl modify database -d EBSCDB -pwfile '+DATA/EBSCDB/orapwEBSCDB'

    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
    # containing two real paths, e.g.:
    # /u01/app/oracle/ebsdb/temp
    # /u01/app/oracle/ebsdb/plsql

    Then run the script in its two modes:

    perl $ORACLE_HOME/appsutil/bin/txkCfgUtlfileDir.pl -contextfile=<DB_CONTEXT> -mode=setUtlFileDir
    perl $ORACLE_HOME/appsutil/bin/txkCfgUtlfileDir.pl -contextfile=<DB_CONTEXT> -mode=syncUtlFileDir

    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

    1. 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.
    2. Verify the bootstrap files yourself. Do not assume the OLR is <hostname>.olr — check cdata/localhost/ and verify with ocrcheck -local.
    3. Stale checkpoints block retries silently. Clear ROOTHAS_STACK / ROOTCRS_STACK before re-running rootupgrade.sh.
    4. After an Oracle Restart GI upgrade, register ASM (srvctl add asm / srvctl start asm) before you trust any storage-related tool.
    5. Use dbua -keepEvents for EBS, and fix X11 forwarding across the user switch before you launch.
    6. The PDB conversion’s hardest part is password files — ORA-65066, OPW-00029, and ASM password-file placement. Budget time for it.
    7. 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.

  • Oracle EBS R12.2 Clone Redirects to PROD After Login: Diagnosing Stale Configuration References

    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:

    1. They keep PROD host references alive inside config.xml.
    2. They confuse the EBS service control and the Admin Server’s view of the cluster.
    3. 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?
    grep -E "<name>|<listen-address>" \
    $EBS_DOMAIN_HOME/config/config.xml | grep -iE "oacore|oafm|forms|server"
    # What does EBS think the nodes are?
    sqlplus apps/<pwd> <<'EOF'
    COL node_name FORMAT a20
    COL server_address FORMAT a18
    COL support_cp FORMAT a10
    COL support_web FORMAT a10
    COL support_admin FORMAT a13
    SELECT node_name,
    NVL(server_address,'-') AS server_address,
    NVL(support_cp,'-') AS support_cp,
    NVL(support_web,'-') AS support_web,
    NVL(support_admin,'-') AS support_admin
    FROM fnd_nodes
    ORDER BY node_name;
    EOF

    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:

    grep -E "s_webentryhost|s_webentrydomain|s_webentryurlprotocol|s_active_webport|s_url_protocol|s_login_page" \
    $CONTEXT_FILE

    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:

    10.x.x.x clone-apps.example.com clone-apps

    Verify resolution from each node and re-test:

    for n in appnode1 appnode2 appnode3; do
    echo "== $n =="; ssh $n "nslookup clone-apps.example.com | tail -3"
    done

    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 on clone-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.