Error [A command line option has attempted to allow or enable the Security Manager]

A command line option has attempted to allow or enable the Security Manager(JDK 24 以降で JVM が起動しない)の直し方

FIX SUMMARY verified
Applies when
eclipse-temurin@sha256:68868d04fa9cfd5f5c6abec0b5cef86d8de2bf9c62c37c7d3e4f0f80f5cfd7ff-Djava.security.manager=allow left in JVM args from the JDK 18 workaround; JEP 486 makes the same flag refuse VM startup from JDK 24

Verified: reproduced in eclipse-temurin@sha256:68868d04fa9cfd5f5c6abec0b5cef86d8de2bf9c62c37c7d3e4f0f80f5cfd7ff, then the A command line option has attempted to allow or enable the Security Manager signature was gone after the fix (exit 0).

Java 21 まで動いていたアプリを JDK 25 で起動すると、アプリのログが 1 行も出ないまま、次のエラーで終わることがあります。

Error occurred during initialization of VM
java.lang.Error: A command line option has attempted to allow or enable the Security Manager. Enabling a Security Manager is not supported.
	at java.lang.System.initPhase3(java.base@25.0.3/System.java:1970)

コピペ検索用の表記ゆれです。同じ原因が、渡し方によって別の見え方をします。

  • A command line option has attempted to allow or enable the Security Manager
  • Error occurred during initialization of VM(この 1 行だけがログに残ることもあります)
  • UnsupportedOperationException: Setting a Security Manager is not supported(JDK 24 以降・実行時)
  • UnsupportedOperationException: The Security Manager is deprecated and will be removed in a future release(JDK 18〜23・実行時)

スタックトレースは java.lang.System.initPhase3 を指していて、あなたのコードを 1 行も指していません。それもそのはずで、この失敗は main() に到達する前に起きています。犯人はコードではなく、JVM に渡っている 1 個の起動フラグです。

直し方の要点を先に

-Djava.security.manager の指定を JVM 引数から消します。

# 落ちる
java -Djava.security.manager=allow -jar app.jar

# 起動する
java -jar app.jar

値は allow とは限りません。JDK 24 以降は、Security Manager を有効化しうる指定がすべて起動時に拒否されます。次の 5 つは、どれも同じエラーになります(JDK 24 と 25 で実測)。

指定JDK 24 / 25
-Djava.security.manager(値なし)起動しない
-Djava.security.manager=(空文字)起動しない
-Djava.security.manager=allow起動しない
-Djava.security.manager=default起動しない
-Djava.security.manager=<クラス名>起動しない
-Djava.security.manager=disallow起動する(警告も出ない)

=allow が圧倒的に多いはずです。これは JDK 18 で System.setSecurityManager が既定で拒否されるようになったときの定番の対処で、18 から 23 までは、これで従来どおり動きます。

フラグを消して起動するなら、それで終わりです。消したら今度は実行時に例外が出たなら、アプリが本当に Security Manager を使っています。その場合は フラグを消したあとに出る例外 へ進んでください。

なぜ 24 で起動しなくなるのか

Security Manager は JDK 17 で非推奨になり(JEP 411)、JEP 486 が JDK 24 で恒久的に無効化しました。

java.lang.SecurityManager は JDK 25 にも残っていて、参照するコードはコンパイルも実行もできます(marked for removal の警告が出るだけです)。削除されたのではなく、無くなったのは「有効化する手段」のほうです。API そのものの削除は、将来の版に予告されている段階にあります。

段階を追うと、同じ起動コマンドの結果が版で 3 回変わります。

JDKSystem.setSecurityManager を呼ぶだけ-Djava.security.manager=allow を付ける
17成功する成功する(警告つき)
18〜23UnsupportedOperationException(既定が拒否に変わった)成功する(警告つき)。これが当時の対処
24 / 25 / 26UnsupportedOperationException(文言が変わる)JVM が起動しない

18 で拒否されるようになったとき、検索して出てくる答えは「-Djava.security.manager=allow を付けろ」でした。それは 23 まで正しく動きます。そして 24 以降、その同じフラグが起動を止めます。当時「正しく対処した」チームほど、JDK を上げた瞬間に起動しないアプリを手にすることになる。

境界は LTS の切れ目に乗っています。JDK 21 には逃げ道があり、次の LTS である JDK 25 には無い。21 から 25 へ跳ぶ人が、そのまま踏みます。

フラグを消したあとに出る例外

フラグを消しても、System.setSecurityManager を呼んでいるコードが残っていれば、今度は実行時に落ちます。

Exception in thread "main" java.lang.UnsupportedOperationException: Setting a Security Manager is not supported
	at java.base/java.lang.System.setSecurityManager(System.java:304)
	at Deep$Lib.install(Deep.java:2)
	at Deep$Framework.boot(Deep.java:3)
	at Deep.main(Deep.java:4)

VM 起動エラーはあなたのコードを 1 行も指しませんでしたが、この例外は呼び出し元を名指しします。見るのは System.setSecurityManager の直下のフレームです。上の例なら Deep$Lib.install。いちばん下の行(Deep.main)はプログラムの入口で、テストランナーやフレームワークのブートストラップが並ぶだけなので、そこを直しても意味がありません。

フラグを外すことは、原因の場所を JVM に吐かせる手順でもあります。-Djava.security.manager=allow を付けたままだと、いつまでもここへ辿り着けません。

JDK 24 以降は Security Manager を有効化できず、API も無効化されています。その呼び出しは、消す前からすでに何も強制していません。 裏を返すと、Security Manager で実際に何かを守っていたなら、それは JDK 24 へ上げると決めた時点で失われています。呼び出しを消すのは、失効した残骸の後片付けであって、防御の移行ではない。何を守っていたのか(サンドボックス、System.exit の禁止、テスト中のファイル・ネットワーク遮断など)によって、置き換え先はそれぞれ別の仕組みになります。この記事では扱いません。

名指しされたのが依存ライブラリの中なら、そのライブラリの changelog で、JDK 24 以降に対応した版があるかを確認してください(どの版で解消するかは検証していません)。

なお、実行時例外の文言は 24 で変わります。

JDK実行時例外のメッセージ
18〜23The Security Manager is deprecated and will be removed in a future release
24 以降Setting a Security Manager is not supported

古い文言で検索してたどり着いた記事は、新しい文言の読者を助けません。逆も同じです。

フラグはどこに隠れているか

java コマンドに直接書かれているとは限りません。環境変数から注入されても、まったく同じ VM 起動拒否になります。

$ JAVA_TOOL_OPTIONS=-Djava.security.manager=allow java -version
Picked up JAVA_TOOL_OPTIONS: -Djava.security.manager=allow
Error occurred during initialization of VM
java.lang.Error: A command line option has attempted to allow or enable the Security Manager. ...

先頭の Picked up ... の 1 行が、注入元を教えてくれます。ただしこの行は、環境変数から来たときにしか出ません。経路と手がかりの対応は次のとおりです(JDK 25 で実測)。

経路出る行
JAVA_TOOL_OPTIONSPicked up JAVA_TOOL_OPTIONS: ...
_JAVA_OPTIONSPicked up _JAVA_OPTIONS: ...
JDK_JAVA_OPTIONSNOTE: Picked up JDK_JAVA_OPTIONS: ...(接頭辞が違う)
@argfile(引数ファイル)何も出ない
ラッパースクリプト・systemd の ExecStart何も出ないjava に直接渡っているため)

Picked up が出ていないなら環境変数由来ではない、とまでは言えます。JVM 引数の混入ではない、とは言えません。 探す場所は、だいたい次のあたりです。

  • DockerfileENV JAVA_TOOL_OPTIONS
  • CI の環境変数設定
  • java @jvm.options のような引数ファイル
  • 起動スクリプト、systemd の unit ファイル
  • アプリケーションサーバの setenv.sh 相当

JAVA_OPTS という名前も広く使われていますが、これは JVM が自動で読む変数ではありません。起動スクリプトが展開して java に渡しているときだけ効きます。 設定した覚えがあるのに Picked up が出ないなら、その変数を誰が展開しているかを追ってください。

フラグを消せないときの暫定策

注入元が組織共通のベースイメージや CI テンプレートにあって、自分の権限では直せないことがあります。その場合は、値を disallow に変えると VM は起動します。

java -Djava.security.manager=disallow -jar app.jar

disallow は「Security Manager を使わない」という宣言で、JDK 18 以降の既定と同じ状態です。有効化しようとしていないので拒否されません(JDK 24 と 25 の両方で、警告も出さずに起動することを確認しました)。

これで動くのは、フラグだけが惰性で残っていて、System.setSecurityManager を誰も呼んでいない場合です。呼び出しが残っていれば、disallow を付けても実行時 UnsupportedOperationException になります(実測済み)。恒久策はフラグを消すことで、disallow はそこへ辿り着くまでの足場に留めてください。

JDK は要らなくなったフラグを 3 通りに扱う

同じ「移行で要らなくなったフラグ」でも、JDK の扱い方が 1 つではないところが、この一連のエラーを分かりにくくしています。同じフラグでも、版が 1 つ違うだけで扱いが変わります。

JDK の扱い起動実例
非推奨(警告して動く)起動する-XX:+UseBiasedLocking(JDK 17:Option UseBiasedLocking was deprecated in version 15.0
廃止(受理して黙殺する)起動する。指定は効いていない--illegal-access=permit(JDK 17)/-XX:+UseBiasedLockingJDK 18 だけIgnoring option ...; support was removed in 18.0
拒否起動しない-Djava.security.manager=allow(JDK 24 以降)/-XX:+UseBiasedLocking(JDK 19 以降:Unrecognized VM option

上の 2 つは、警告を 1 行出しただけで JVM が起動してしまいます。「フラグは効いているはず」と思い込んだまま先へ進めるのがここで、--illegal-access=permit を足しても InaccessibleObjectException が 1 文字も変わらないのはこの状態です(詳しくは InaccessibleObjectException: module java.base does not “opens java.util” の直し方)。動いているのは JVM であって、アプリのエラーは残ったままになります。

このエラーは 3 つ目です。起動を拒むぶん気づくのは早いかわりに、エラーがコードのどこも指さないので、どこを直せばいいのかが分かりません。

なお、3 つ目の中にも 2 種類あります。Unrecognized VM optionJDK がその名前を知らない(フラグが完全に消えた)。-Djava.security.manager=allow のほうは JDK が名前を知っていて、そのうえで拒む。だから文言も別物で、切り分けの手がかりになります。

切り分け

  • アプリのログが 1 行も出ない/Error occurred during initialization of VM だけが残るmain() より前の失敗です。アプリのコードではなく JVM 引数を疑ってください。java -version を同じ引数・同じ環境変数で実行すると、アプリ抜きで再現できます。
  • 起動コマンドを見てもフラグが無い → 環境変数か引数ファイルから入っています。Picked up ... の行が出ていれば環境変数、出ていなければ引数ファイルかラッパースクリプトを疑ってください。
  • フラグを消したら、今度は UnsupportedOperationException → 想定どおりです。アプリか依存ライブラリが実際に System.setSecurityManager を呼んでいます。System.setSecurityManager の直下のフレームが呼び出し元です。
  • JDK 21 では同じ設定で動いていた → 21 は =allow を受け付ける最後の LTS です。24 で拒否に変わりました。
  • エラーが Unrecognized VM option → 別のフラグの話です。そちらは JDK が名前ごと知らないオプションで、Security Manager とは無関係に起動が失敗しています。
  • --illegal-access=permit を足しても例外が変わらない → 別件です。InaccessibleObjectException の記事を参照してください。
  • UnsupportedClassVersionError が出る → こちらはクラスファイルの版の話です。has been compiled by a more recent version of the Java Runtime の直し方へ。

検証環境

  • eclipse-temurin@sha256:68868d04...openjdk version "25.0.3" LTS)、ネットワーク無し
  • 再現:System.setSecurityManager(new SecurityManager()) を含むクラスを -Djava.security.manager=allow 付きで実行し、A command line option has attempted to allow or enable the Security Manager が出て終了コード 1
  • 修正:フラグと setSecurityManager の呼び出しを両方外して終了コード 0・シグネチャ消滅。フラグだけ外した状態も実行し、実行時 UnsupportedOperationException に変わることを確認しています(=フラグを外すだけでは直りません)
  • 値の表(値なし/空文字/allowdefault/クラス名/disallow)は、JDK 24 と 25 の両方で 6 通りとも実行しました
  • disallow を付けたうえで setSecurityManager を呼ぶと実行時例外になること、3 層の呼び出しでスタックトレースの直下のフレームが呼び出し元になることも、個別に実行して確認しています
  • 注入経路の表(JAVA_TOOL_OPTIONS / _JAVA_OPTIONS / JDK_JAVA_OPTIONS / @argfile)も同じイメージで実測しました
  • java.lang.SecurityManager が JDK 25 で解決でき、System.getSecurityManager()null を返すこと(=削除ではない)も確認済みです
  • 版の表(17 / 18〜23 / 24 / 25 / 26)は eclipse-temurin の 10 版でそれぞれ実行して確認しました
  • --illegal-access=permit が受理・黙殺されることは、姉妹記事のケース(JDK 17)で確認済みです

Maven Surefire の argLine や Gradle の jvmArgs からフラグが渡る経路は、機構が同じ(JVM に引数が届くか)ことを根拠にした説明であり、ビルドツール経由での再現は通していません。どのフレームワークやライブラリが -Djava.security.manager=allow を要求していたかも確認していません。

検証(machine-verified)

この修正は eclipse-temurin@sha256:68868d04fa9cfd5f5c6abec0b5cef86d8de2bf9c62c37c7d3e4f0f80f5cfd7ff のバージョン固定コンテナ内で再現し、修正後に A command line option has attempted to allow or enable the Security Manager のシグネチャが消えることを機械で確認しています。

verify — run-case.mjs
$ node run-case.mjs jvm/securitymanager-not-supported-jdk24
● reproduce A command line option has attempted to allow or enable the Security Manager present ✓
● apply fix exit 0
● re-run A command line option has attempted to allow or enable the Security Manager gone ✓
PASS verified · eclipse-temurin@sha256:68868d04fa9cfd5f5c6abec0b5cef86d8de2bf9c62c37c7d3e4f0f80f5cfd7ff · signature gone

確認したのは上のイメージの中だけです。別の環境で直らなかった、記述が違う、という場合は 報告してください(対象と検証イメージは件名・本文に入ります)。