【完全ガイド】ORA-04061: existing state of … has been invalidated の原因と解決方法|ORA-04068 との違い・グローバル状態・EBR 徹底解説

【完全ガイド】ORA-04061: existing state of … has been invalidated の原因と解決方法|ORA-04068 との違い・グローバル状態・EBR 徹底解説

Oracle 開発者・DBA が本番運用中のパッケージ更新で頻繁に遭遇するエラー:

ORA-04061: existing state of package body "APPS.PO_REQAPPROVAL_INIT1" has been invalidated
ORA-04065: not executed, altered or dropped package body "APPS.PO_REQAPPROVAL_INIT1"
ORA-06508: PL/SQL: could not find program unit being called: "APPS.PO_REQAPPROVAL_INIT1"

**「既存状態が無効化された」**というシンプルなメッセージ。Oracle のパッケージ状態管理における重要なエラーです:

  • セッションが保持していたパッケージ状態の無効化
  • パッケージ変更中のセッションで発生
  • Oracle EBS Workflowで頻発
  • アプリケーションの動作停止
  • 24時間運用の障害

このエラーの本質は、セッションのパッケージ状態と現在のパッケージ定義が不整合:

1. セッション A がパッケージ P を実行
2. P のグローバル変数 / 定数 / カーソル状態を保持
3. 別セッション B が P を再コンパイル/変更
4. セッション A が再度 P を呼び出し
5. → 保持状態と新しい P が不整合
6. → ORA-04061 発生
目次

ORA-04061 vs ORA-04068 の決定的違い

極めて混同しやすい姉妹エラー:

ORA-04061: existing state has been INVALIDATED
  → 状態が無効化された
  → コンパイル時に検出
  → 「まだ状態は残っている、しかし使えない」

ORA-04068: existing state has been DISCARDED
  → 状態が破棄された
  → 実行時に発生
  → 「状態自体が消えた」

両者は通常セットで発生、原因は同じ(パッケージ変更)。

現場で最も典型的なのは、Oracle E-Business Suite (EBS):

[WF_ERROR] ERROR_MESSAGE=3835:
Error '-20002 - ORA-20002: 2018: Unable to generate the notification XML.
ORA-04061: existing state of package body "APPS.PO_WF_PO_NOTIFICATION" has been invalidated

シナリオ:
1. EBS Workflow が動作中
2. APPS.PO_WF_PO_NOTIFICATION パッケージ更新
3. Workflow セッションが再アクセス
4. → ORA-04061 → PO 承認通知メール失敗

多くの日本語記事が「再実行せよ」で終わりますが、実務では:

  • ORA-04061 vs ORA-04068 の厳密な違い
  • 併発エラー4種ORA-04065, ORA-04063, ORA-06508, ORA-06512
  • グローバル状態を持つパッケージの根本問題
  • PRAGMA SERIALLY_REUSABLE による回避
  • Edition-Based Redefinition (EBR) の活用
  • DBMS_SESSION.RESET_PACKAGE の使い所と限界
  • Rails ActiveRecord の接続プールリセット
  • Oracle datapatch 中の発生(19.10 アップグレード等)
  • Oracle EBS AutoConfig / adadmin での対処
  • Oracle 26ai の SESSION_EXIT_ON_PACKAGE_STATE_ERROR 新機能

さらに、根本原因を理解することが重要です:

グローバル状態を持つパッケージの問題:
PACKAGE state = セッションごとに独立
→ セッションで保持したまま、パッケージ再コンパイル
→ 状態と定義の不整合
→ ORA-04061

解決方針:
1. グローバル状態を持たないパッケージ設計
2. PRAGMA SERIALLY_REUSABLE で毎回リセット
3. Functions を使い状態を持たない
4. EBR で古い定義を維持

本記事では、ORA-04061: existing state of has been invalidated完全な原因と解決方法を、リファレンスとして実用的に整理します。ORA-04068 との違い、10大発生パターン、7つの解決策、EBR、Rails/Java/Python 対応、実践シナリオ、FAQまで完全網羅。この1本で ORA-04061 に冷静に対処できるようになります。


結論:再実行 or グローバル状態排除

時間がない方向けに、最速の対処を先に示します。

エラーメッセージの読み方

ORA-04061: existing state of package body "SCHEMA.PACKAGE" has been invalidated
                                            ↑
                                    無効化されたオブジェクト

= セッションが保持していた状態と、変更されたパッケージ定義が不整合

最速の対処

即時対処: 再実行(同じ呼び出しをリトライ)
  → Oracle が状態を自動的に再初期化
  → 通常成功

根本対処:
1. パッケージからグローバル状態を除去
2. PRAGMA SERIALLY_REUSABLE 追加
3. Functions で状態を持たない設計

併発エラー4種

エラー意味
ORA-04061状態が無効化された
ORA-04065パッケージが変更/削除された
ORA-06508プログラムユニット発見不可
ORA-06512発生位置(スタックトレース)

通常セットで発生

7つの解決策

#手法使う場面
再実行一時的対処
グローバル状態除去根本対処
PRAGMA SERIALLY_REUSABLEパッケージ設計
Functions 使用状態レス化
Edition-Based Redefinition高度な回避
Rails 接続プールリセットRails 対応
SESSION_EXIT_ON_PACKAGE_STATE_ERROR26ai 新機能

ORA-04061 vs ORA-04068

ORA-04061: INVALIDATED(無効化)
  - コンパイル時に検出
  - 状態はまだあるが使えない

ORA-04068: DISCARDED(破棄)
  - 実行時に発生
  - 状態自体が消えた

通常両方セットで発生。

詳細は以下で解説します。


Oracle パッケージの状態管理

パッケージ状態とは

パッケージは以下を持つ:

CREATE OR REPLACE PACKAGE my_pkg IS
  g_counter NUMBER := 0;  -- グローバル変数(状態)
  g_config VARCHAR2(100); -- グローバル変数
  
  PROCEDURE proc_a;
END my_pkg;
/

各セッションが独自の状態を保持:

セッション A: g_counter = 5
セッション B: g_counter = 10
セッション C: g_counter = 3

状態の永続性

通常のパッケージ:

  • セッション終了まで状態保持
  • 同じセッションからの再呼び出しで同じ状態

PRAGMA SERIALLY_REUSABLE:

  • 各呼び出し毎に状態リセット
  • サーバー再利用性向上

状態の無効化契機

1. パッケージ本体 (BODY) の変更/再コンパイル
2. パッケージ仕様 (SPEC) の変更/再コンパイル
3. 依存オブジェクトの変更
4. DBMS_SESSION.RESET_PACKAGE 呼び出し

→ セッションが次にパッケージ呼び出し
→ ORA-04061 or ORA-04068

【原因①】パッケージ再コンパイル中(最頻出)

シナリオ

セッション A: my_pkg 使用中(状態保持)
セッション B (DBA): ALTER PACKAGE my_pkg COMPILE BODY;
セッション A: 再度 my_pkg 呼び出し
→ ORA-04061 発生

対処

A. 再実行(Oracle 自動回復):

BEGIN
  my_pkg.proc_a;
END;
/
-- 初回: ORA-04061
-- 再度実行: 通常成功(状態が再初期化される)

B. アプリで自動リトライ:

def call_proc
  ActiveRecord::Base.connection.execute("BEGIN my_pkg.proc_a; END;")
rescue ActiveRecord::StatementInvalid => e
  if e.message.include?("ORA-04061") && (retries ||= 0) < 3
    retries += 1
    retry
  end
  raise
end

【原因②】Oracle E-Business Suite Workflow

症状(現場頻発)

ERROR: Action Required: Error3240: Error '-4061'
ORA-04061: existing state of package body "APPS.PO_WF_PO_NOTIFICATION" 
           has been invalidated
ORA-04065: not executed, altered or dropped package body
ORA-06508: PL/SQL: could not find program unit being called

PO 承認、Absence 承認、Appraisal 承認の通知メール失敗。

対処(Oracle 公式手順)

A. Notification Mailer 再起動 + Shared Pool フラッシュ:

-- SYSDBA で
SQL> ALTER SYSTEM FLUSH SHARED_POOL;

B. Concurrent Manager / Apache 停止 → adadmin で APPS 再コンパイル:

# EBS 環境で
$COMMON_TOP/admin/scripts/$SID/adstpall.sh apps/apps

# adadmin 起動
adadmin
# Recompile APPS Schema オプション実行

# 再起動
$COMMON_TOP/admin/scripts/$SID/adstrtal.sh apps/apps

C. 定期的な INVALID オブジェクト再コンパイル:

BEGIN
  UTL_RECOMP.recomp_parallel(4, 'APPS');
END;
/

INVALID オブジェクト関連は PLS-00905: object is invalid の記事、ORA-04068: existing state of packages の記事も参照してください。


【原因③】グローバル状態を持つパッケージ(根本原因)

問題のコード

CREATE OR REPLACE PACKAGE sith_manager AS
  gc_default_name CONSTANT VARCHAR2(200) := 'Darth Ora';
  
  PROCEDURE add_sith(i_name VARCHAR2);
END sith_manager;
/

CREATE OR REPLACE PACKAGE BODY sith_manager AS
  PROCEDURE add_sith(i_name VARCHAR2) IS
  BEGIN
    INSERT INTO sith (name) 
    VALUES (NVL(i_name, gc_default_name));
  END;
END sith_manager;
/

問題: gc_default_name が定数でもパッケージ状態を持つと Oracle が判定。

解決A: PRAGMA SERIALLY_REUSABLE

CREATE OR REPLACE PACKAGE sith_manager AS
  PRAGMA SERIALLY_REUSABLE;   -- ← 追加
  
  gc_default_name CONSTANT VARCHAR2(200) := 'Darth Ora';
  PROCEDURE add_sith(i_name VARCHAR2);
END sith_manager;
/

CREATE OR REPLACE PACKAGE BODY sith_manager AS
  PRAGMA SERIALLY_REUSABLE;   -- ← 本体にも
  ...
END;
/

効果: 各呼び出し毎にリセット、状態問題回避。

解決B: 状態のない Functions

-- 状態を持たない設計
CREATE OR REPLACE FUNCTION get_default_name RETURN VARCHAR2 IS
BEGIN
  RETURN 'Darth Ora';
END;
/

-- パッケージ本体
CREATE OR REPLACE PACKAGE BODY sith_manager AS
  PROCEDURE add_sith(i_name VARCHAR2) IS
  BEGIN
    INSERT INTO sith (name) 
    VALUES (NVL(i_name, get_default_name));
  END;
END sith_manager;
/

【原因④】Rails / Django マイグレーション中

シナリオ

# Rails マイグレーション実行中
class CompilePackages < ActiveRecord::Migration[8.0]
  def up
    execute "ALTER PACKAGE my_pkg COMPILE BODY"
  end
end

# 一方、Web アプリが my_pkg 使用中
# → ORA-04061 発生

解決

A. マイグレーション後の接続プールリセット:

class CompilePackages < ActiveRecord::Migration[8.0]
  def up
    execute "ALTER PACKAGE my_pkg COMPILE BODY"
    
    # 接続プールを全リセット(全セッションで状態リセット)
    ActiveRecord::Base.connection_pool.disconnect!
  end
end

B. デプロイ順序管理(Kamal 等):

# 1. 一時的にアプリ停止
kamal app stop

# 2. マイグレーション
kamal migrate

# 3. アプリ再起動
kamal app start

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、rails db:migrate 使い方の記事、Kamal 2 デプロイの記事も参照してください。


【原因⑤】CI/CD デプロイ中

シナリオ

# デプロイ中
- name: Deploy PL/SQL
  run: |
    sqlplus $DB_USER/$DB_PW <<EOF
    @deploy_packages.sql
    EOF

# アプリは継続稼働 → ORA-04061 頻発

解決

A. デプロイ時にアプリ一時停止(推奨)。 B. アプリでリトライロジックC. Edition-Based Redefinition 使用(後述)。


【原因⑥】Oracle datapatch 中

症状(19c → 19.10 パッチ適用中)

Patch 31281355 apply: WITH ERRORS
- ORA-04068: existing state of packages has been discarded
- ORA-04061: existing state of package body "SYS.DBMS_AQADM_SYS" has been invalidated
- ORA-04065: not executed, altered or dropped package body "SYS.DBMS_AQADM_SYS"
- ORA-06508: PL/SQL: could not find program unit being called
- ORA-06512: at "SYS.DBMS_AQADM", line 1090

Oracle 内部の rdbms/admin/catsnmp.sql 実行時。

解決(Oracle 公式)

A. datapatch 前に隠しパラメータ設定:

ALTER SYSTEM SET "_srvntfn_job_deq_timeout" = 0 SCOPE=SPFILE;
-- 再起動

B. パッチ適用後リセット:

ALTER SYSTEM RESET "_srvntfn_job_deq_timeout" SCOPE=BOTH;

C. DB 再起動

Unpublished BUG 32174571 として Oracle が認識。


【原因⑦】パッケージ内定数変更

シナリオ

-- 変更前
CREATE PACKAGE cfg IS
  MAX_RETRY CONSTANT NUMBER := 3;
END;
/

-- 変更後
CREATE OR REPLACE PACKAGE cfg IS
  MAX_RETRY CONSTANT NUMBER := 5;   -- 変更
END;
/

-- 使用中セッション → ORA-04061

解決

A. アプリ再起動(接続プールリセット)。 B. DBMS_SESSION.RESET_PACKAGE :

BEGIN
  DBMS_SESSION.RESET_PACKAGE;
END;
/
-- 現セッションの全パッケージ状態リセット

⚠️ 副作用大、慎重に。


【原因⑧】型変更(Object Type)

シナリオ

-- Object Type 変更
ALTER TYPE addr_t ADD ATTRIBUTE (country VARCHAR2(50)) CASCADE;

-- 使用中セッション → ORA-04061 の可能性

解決

A. 使用中セッション終了 → 再接続B. Edition-Based Redefinition(後述)。


【原因⑨】Java Callable Statement

シナリオ

// Java アプリで長時間セッション
CallableStatement cs = conn.prepareCall("{call my_pkg.proc_a}");
cs.execute();

// 別セッションで my_pkg 更新
// → 次回 cs.execute() で ORA-04061

解決

try {
    cs.execute();
} catch (SQLException e) {
    if (e.getErrorCode() == 4061) {
        // 接続再取得
        conn.close();
        conn = ds.getConnection();
        cs = conn.prepareCall("{call my_pkg.proc_a}");
        cs.execute();   // リトライ
    }
}

【原因⑩】PL/SQL 呼び出し内

シナリオ

-- パッケージ A が B を呼ぶ
CREATE PACKAGE pkg_a IS
  PROCEDURE do_work;
END;
/

CREATE PACKAGE BODY pkg_a IS
  PROCEDURE do_work IS
  BEGIN
    pkg_b.helper;   -- B が変更されると ORA-04061
  END;
END;
/

解決

両パッケージの依存関係管理共通の再デプロイ


診断ツール完全リファレンス

エラーコード確認

EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE = -4061 THEN
      DBMS_OUTPUT.PUT_LINE('State invalidated');
    ELSIF SQLCODE = -4068 THEN
      DBMS_OUTPUT.PUT_LINE('State discarded');
    END IF;
END;

V$SESSION でセッション確認

SELECT sid, serial#, username, module, status, prev_sql_id
FROM v$session
WHERE username = 'APP_USER';

INVALID オブジェクト

SELECT owner, object_name, object_type, status
FROM all_objects
WHERE status = 'INVALID'
  AND owner = 'APPS';

DBMS_SESSION 状態確認

-- 現セッションの状態リセット
BEGIN
  DBMS_SESSION.RESET_PACKAGE;
END;
/
-- 全パッケージのグローバル状態クリア

Shared Pool フラッシュ(緊急対応)

ALTER SYSTEM FLUSH SHARED_POOL;
-- ⚠️ パフォーマンスに影響、慎重に

7つの解決策 完全リファレンス

解決策① 再実行(自動回復)

-- Oracle は次回呼び出しで状態を自動再初期化
BEGIN
  my_pkg.proc_a;   -- 初回: ORA-04061
END;
/

BEGIN
  my_pkg.proc_a;   -- 2 回目: 通常成功
END;
/

多くの場合これで解決

解決策② グローバル状態除去

-- ❌ グローバル状態あり
CREATE PACKAGE bad_pkg AS
  g_counter NUMBER := 0;
  PROCEDURE increment;
END;
/

-- ✅ 状態を持たない設計
CREATE PACKAGE good_pkg AS
  PROCEDURE increment(io_counter IN OUT NUMBER);
END;
/

CREATE PACKAGE BODY good_pkg AS
  PROCEDURE increment(io_counter IN OUT NUMBER) IS
  BEGIN
    io_counter := io_counter + 1;
  END;
END;
/

解決策③ PRAGMA SERIALLY_REUSABLE

CREATE PACKAGE reusable_pkg AS
  PRAGMA SERIALLY_REUSABLE;
  
  g_config VARCHAR2(100);
  PROCEDURE proc_a;
END;
/

CREATE PACKAGE BODY reusable_pkg AS
  PRAGMA SERIALLY_REUSABLE;
  
  PROCEDURE proc_a IS
  BEGIN
    g_config := 'value';   -- 呼び出し毎にリセット
  END;
END;
/

制約:

  • SPEC と BODY 両方に必要
  • サーバーサイドセッションでのみ効果
  • 一部の PL/SQL 機能制限

解決策④ Functions 使用

-- Function は状態を持たない
CREATE FUNCTION get_config RETURN VARCHAR2 IS
BEGIN
  RETURN 'value';
END;
/

-- パッケージから利用
CREATE PACKAGE BODY logic_pkg AS
  PROCEDURE do_work IS
    v_config VARCHAR2(100) := get_config;
  BEGIN
    ...
  END;
END;
/

解決策⑤ Edition-Based Redefinition (EBR)

Oracle 11g R2+ の高度な機能:

-- 新エディション作成
CREATE EDITION new_edition;

-- 新エディションでパッケージ変更
ALTER SESSION SET EDITION = new_edition;
CREATE OR REPLACE PACKAGE my_pkg AS ... END;

-- 既存セッションは旧エディションで動作継続
-- 新セッションのみ新エディション
-- → ORA-04061 回避

アップグレード後:

-- 全セッションで新エディション使用
ALTER DATABASE DEFAULT EDITION = new_edition;

解決策⑥ Rails 接続プールリセット

# デプロイ後
ActiveRecord::Base.connection_pool.disconnect!

# または個別接続
ActiveRecord::Base.connection.reconnect!

# マイグレーション内で
class ForceReconnect < ActiveRecord::Migration[8.0]
  def up
    # パッケージ変更
    execute "ALTER PACKAGE my_pkg COMPILE BODY"
    
    # 接続プール全リセット
    ActiveRecord::Base.connection_pool.disconnect!
  end
end

解決策⑦ SESSION_EXIT_ON_PACKAGE_STATE_ERROR (26ai)

Oracle 26ai 新機能:

-- セッションレベル
ALTER SESSION SET session_exit_on_package_state_error = TRUE;

-- システムレベル
ALTER SYSTEM SET session_exit_on_package_state_error = TRUE;

効果:

ORA-04061/04068 発生時
→ セッション自動切断
→ アプリの接続再取得ロジックで自動回復
→ silent data corruption 防止

推奨環境: 26ai 以降 + 接続プール使用アプリ。


Rails / Java / Python 対応

Rails ActiveRecord

自動リトライ + 接続リセット:

class PackageCaller
  def self.call_with_retry(sql, max_retries: 3)
    retries = 0
    begin
      ActiveRecord::Base.connection.execute(sql)
    rescue ActiveRecord::StatementInvalid => e
      if (e.message.include?("ORA-04061") || e.message.include?("ORA-04068")) \
         && retries < max_retries
        retries += 1
        Rails.logger.warn "Package state error, retry #{retries}"
        ActiveRecord::Base.connection.reconnect!
        retry
      end
      raise
    end
  end
end

# 使用
PackageCaller.call_with_retry("BEGIN my_pkg.proc_a; END;")

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事も参照してください。

Java (JDBC)

public void callWithRetry(String sql, int maxRetries) throws SQLException {
    int retries = 0;
    while (true) {
        try (Statement stmt = conn.createStatement()) {
            stmt.execute(sql);
            return;
        } catch (SQLException e) {
            if ((e.getErrorCode() == 4061 || e.getErrorCode() == 4068) 
                && retries < maxRetries) {
                retries++;
                logger.warn("Package state error, retry " + retries);
                conn.close();
                conn = ds.getConnection();
            } else {
                throw e;
            }
        }
    }
}

Python (oracledb)

import oracledb

def call_with_retry(dsn, user, pw, sql, max_retries=3):
    for attempt in range(max_retries):
        try:
            with oracledb.connect(user=user, password=pw, dsn=dsn) as conn:
                cursor = conn.cursor()
                cursor.execute(sql)
                return
        except oracledb.DatabaseError as e:
            error_obj, = e.args
            if error_obj.code in (4061, 4068) and attempt < max_retries - 1:
                print(f"Package state error, retry {attempt + 1}")
                continue
            raise

実践シナリオ

シナリオ1:Oracle EBS Workflow 障害対応

-- 1. INVALID オブジェクト確認
SELECT owner, object_name, object_type
FROM dba_objects
WHERE status = 'INVALID' AND owner = 'APPS';

-- 2. Notification Mailer 停止
-- (Concurrent Manager 経由)

-- 3. Shared Pool フラッシュ
SQL> CONN / AS SYSDBA
SQL> ALTER SYSTEM FLUSH SHARED_POOL;

-- 4. APPS スキーマ再コンパイル
BEGIN
  UTL_RECOMP.recomp_parallel(4, 'APPS');
END;
/

-- 5. Notification Mailer 再起動

-- 6. Workflow 再実行確認

シナリオ2:本番デプロイでの回避

# 1. アプリケーション一時停止
kamal app stop

# 2. パッケージ更新
sqlplus $DB_USER/$DB_PW <<EOF
@deploy_packages.sql
EOF

# 3. INVALID 確認
sqlplus / as sysdba <<EOF
SELECT COUNT(*) FROM dba_objects WHERE status = 'INVALID';
EOF

# 4. アプリ再起動
kamal app start

Kamal 2 デプロイの詳細は Kamal 2 デプロイの記事を参照してください。

シナリオ3:Rails での耐障害設計

# config/initializers/oracle_retry.rb
module OracleRetry
  def with_package_retry(max_retries: 3)
    retries = 0
    begin
      yield
    rescue ActiveRecord::StatementInvalid => e
      if package_state_error?(e) && retries < max_retries
        retries += 1
        Rails.logger.warn "Package state error, retry #{retries}"
        ActiveRecord::Base.connection.reconnect!
        retry
      end
      raise
    end
  end
  
  private
  
  def package_state_error?(e)
    e.message.include?("ORA-04061") || e.message.include?("ORA-04068")
  end
end

# 使用
include OracleRetry

with_package_retry do
  ActiveRecord::Base.connection.execute("BEGIN my_pkg.proc_a; END;")
end

シナリオ4:EBR による無停止デプロイ

-- 1. 新エディション作成
CREATE EDITION v2;

-- 2. 新エディションでパッケージ変更
ALTER SESSION SET EDITION = v2;
CREATE OR REPLACE PACKAGE my_pkg AS ... END;

-- 3. 既存セッションは v1 で動作継続
-- 新セッションのみ v2

-- 4. アプリ側で切り替え
ALTER SESSION SET EDITION = v2;

-- 5. 全セッション移行後
ALTER DATABASE DEFAULT EDITION = v2;
DROP EDITION v1 CASCADE;

シナリオ5:グローバル状態除去のリファクタリング

-- ❌ 変更前(グローバル状態あり)
CREATE PACKAGE order_pkg AS
  g_current_order_id NUMBER;
  
  PROCEDURE start_order(p_id NUMBER);
  PROCEDURE add_item(p_item VARCHAR2);
END;
/

-- ✅ 変更後(状態レス)
CREATE PACKAGE order_pkg AS
  PROCEDURE add_item(p_order_id NUMBER, p_item VARCHAR2);
END;
/

CREATE PACKAGE BODY order_pkg AS
  PROCEDURE add_item(p_order_id NUMBER, p_item VARCHAR2) IS
  BEGIN
    INSERT INTO order_items(order_id, item) 
    VALUES (p_order_id, p_item);
  END;
END;
/

シナリオ6:Docker Oracle でのテスト

docker exec -it oracle-xe sqlplus scott/tiger <<EOF
CREATE OR REPLACE PACKAGE test_pkg AS
  g_counter NUMBER := 0;
  PROCEDURE increment;
END;
/
CREATE OR REPLACE PACKAGE BODY test_pkg AS
  PROCEDURE increment IS
  BEGIN
    g_counter := g_counter + 1;
    DBMS_OUTPUT.PUT_LINE(g_counter);
  END;
END;
/

BEGIN test_pkg.increment; END;
/
-- Output: 1

-- 別セッションで再コンパイル
-- ALTER PACKAGE test_pkg COMPILE BODY;

BEGIN test_pkg.increment; END;
/
-- ORA-04061 or ORA-04068

BEGIN test_pkg.increment; END;
/
-- 再実行で成功、Output: 1 (リセット済み)
EOF

Docker 関連は docker daemon 接続エラーの記事、Docker no space left on device の記事も参照してください。

シナリオ7:Oracle datapatch 対処

-- 19.10 パッチ適用前
SQL> CONN / AS SYSDBA
SQL> ALTER SYSTEM SET "_srvntfn_job_deq_timeout" = 0 SCOPE=SPFILE;
SQL> SHUTDOWN IMMEDIATE
SQL> STARTUP

-- datapatch 実行
$ORACLE_HOME/OPatch/datapatch -verbose

-- パッチ適用後
SQL> ALTER SYSTEM RESET "_srvntfn_job_deq_timeout" SCOPE=BOTH;
SQL> SHUTDOWN IMMEDIATE
SQL> STARTUP

シナリオ8:CI/CD パイプラインでの対応

- name: Deploy PL/SQL
  run: |
    # 1. アプリ停止
    kubectl scale deployment app --replicas=0
    
    # 2. パッケージ変更
    sqlplus $DB_USER/$DB_PW <<EOF
    @deploy_packages.sql
    EOF
    
    # 3. INVALID 確認
    invalid=$(sqlplus -s / as sysdba <<EOF
    SET HEADING OFF FEEDBACK OFF
    SELECT COUNT(*) FROM dba_objects WHERE status = 'INVALID';
    EXIT;
    EOF
    )
    
    if [ "$invalid" != "0" ]; then
      echo "INVALID objects: $invalid"
      exit 1
    fi
    
    # 4. アプリ再起動
    kubectl scale deployment app --replicas=3

シナリオ9:Autonomous DB での対応

Autonomous DB では
- パッケージ管理は同様
- リトライロジック推奨
- 26ai なら SESSION_EXIT_ON_PACKAGE_STATE_ERROR 活用

シナリオ10:定期監視スクリプト

#!/bin/bash
# monitor_invalid.sh

invalid=$(sqlplus -s / as sysdba <<EOF
SET HEADING OFF FEEDBACK OFF
SELECT COUNT(*) FROM dba_objects WHERE status = 'INVALID';
EXIT;
EOF
)

if [ "$invalid" -gt "0" ]; then
  echo "INVALID objects detected: $invalid" | \
    mail -s "Oracle INVALID Alert" ops@example.com
fi

crontab の詳細は crontab 使い方の記事も参照してください。


トラブルシューティング

再実行しても直らない

INVALID オブジェクトがあるか確認、再コンパイル:

ALTER PACKAGE my_pkg COMPILE BODY;

頻発する場合

根本原因はグローバル状態、リファクタリング検討。

DBMS_SESSION.RESET_PACKAGE の副作用

全パッケージ状態がリセット、業務中は慎重に。

Shared Pool フラッシュの影響

パフォーマンス低下(キャッシュクリア)、業務時間外推奨。

EBR の導入コスト

設計変更大、慎重に計画。

PostgreSQL からの移行

PG は関数ベース、パッケージ状態問題なし。移行時は状態管理見直し。


よくある質問(FAQ)

Q1. ORA-04061 と ORA-04068 の違い

  • 04061: 状態が INVALIDATED(無効化)
  • 04068: 状態が DISCARDED(破棄)
  • 通常セットで発生

Q2. 併発エラー4種

ORA-04061, ORA-04065, ORA-06508, ORA-06512

Q3. 再実行で解決するか

多くの場合 YES。Oracle が自動再初期化。

Q4. グローバル状態を避けるべき

推奨。パッケージ設計時に状態レス化。

Q5. PRAGMA SERIALLY_REUSABLE の使いどころ

頻繁に更新されるパッケージ、状態不要な場合。

Q6. EBR の推奨環境

24時間運用無停止デプロイ必須。

Q7. Rails での対応

リトライロジック + 接続プールリセット

Q8. Java での対応

errorCode == 4061 or 4068 で判定 + 接続再取得。

Q9. Oracle EBS での対処

Notification Mailer 再起動 + Shared Pool フラッシュ

Q10. Oracle 26ai の新機能

SESSION_EXIT_ON_PACKAGE_STATE_ERROR で自動切断。

Q11. パフォーマンスへの影響

再コンパイル時のみ、通常はなし。

Q12. 予防策

  • グローバル状態最小化
  • デプロイ順序管理
  • リトライロジック実装
  • EBR 検討

参考リンク

Oracle 公式


まとめ

ORA-04061: existing state of ... has been invalidated の要点を再整理します。

エラーの本質

セッションが保持していたパッケージ状態と
現在のパッケージ定義が不整合
→ INVALIDATED(無効化)
→ 通常 ORA-04068 とセットで発生

ORA-04061 vs ORA-04068

ORA-04061: INVALIDATED(無効化)
  - コンパイル時に検出
  - 状態はまだあるが使えない

ORA-04068: DISCARDED(破棄)
  - 実行時に発生
  - 状態自体が消えた

エラーメッセージの読み方

ORA-04061: existing state of package body "SCHEMA.PACKAGE" has been invalidated
                                            ↑
                                    無効化されたオブジェクト

併発エラー4種

エラー意味
ORA-04061状態が INVALIDATED
ORA-04065パッケージが変更/削除
ORA-06508プログラムユニット発見不可
ORA-06512発生位置

10大原因

#原因対処
パッケージ再コンパイル中再実行
Oracle EBS Workflowadadmin 再コンパイル
グローバル状態あり除去
Rails マイグレーション接続プールリセット
CI/CD デプロイ一時停止
Oracle datapatch隠しパラメータ
定数変更RESET_PACKAGE
型変更再接続
Java Callable接続再取得
PL/SQL 依存共通デプロイ

7つの解決策

-- ① 再実行(Oracle 自動回復)
BEGIN my_pkg.proc_a; END;

-- ② グローバル状態除去
-- IN OUT パラメータで状態を渡す

-- ③ PRAGMA SERIALLY_REUSABLE
CREATE PACKAGE my_pkg AS
  PRAGMA SERIALLY_REUSABLE;
  ...
END;

-- ④ Functions 使用
-- 状態を持たない Function

-- ⑤ Edition-Based Redefinition
CREATE EDITION new_edition;
ALTER SESSION SET EDITION = new_edition;
# ⑥ Rails 接続プールリセット
ActiveRecord::Base.connection_pool.disconnect!
-- ⑦ 26ai 新機能
ALTER SYSTEM SET session_exit_on_package_state_error = TRUE;

各言語での対応

Rails:  リトライ + connection_pool.disconnect!
Java:   errorCode == 4061/4068 + 接続再取得
Python: DatabaseError.code チェック
共通:   接続再取得ロジック実装

予防のポイント

1. グローバル状態を最小化
2. PRAGMA SERIALLY_REUSABLE 活用
3. Functions で状態レス化
4. リトライロジック実装
5. デプロイ順序管理(アプリ停止)
6. Rails 接続プールリセット
7. EBR 検討(24時間運用)
8. 26ai の新機能活用
9. 定期的な INVALID オブジェクト監視
10. Oracle EBS は Vendor 手順遵守

これらの知識は、Oracle での PL/SQL 開発・パッケージ設計・DBA 運用・Oracle EBS 管理・Rails / Java / Python アプリ運用・CI/CD パイプライン・24時間運用・データベースアップグレードなど、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-04061 に出会っても冷静に的確に対処できるようになります。


本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜26ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。