Error [package javax.mail does not exist]

package javax.mail does not exist の直し方(jakarta.mail-api は 2.0.0 から中身が jakarta.mail になる)

FIX SUMMARY verified
Applies when
eclipse-temurin:17-jdkjakarta.mail:jakarta.mail-api (the Maven coordinate moved from com.sun.mail:javax.mail at 1.6.3, but its classes stayed in javax.mail; the package moves to jakarta.mail only at 2.0.0)bumping jakarta.mail-api from 1.6.x to 2.x while imports still say javax.mail — the coordinate already said jakarta, so the migration looked done

Verified: reproduced in eclipse-temurin:17-jdk, then the package javax.mail does not exist signature was gone after the fix (exit 0).

依存を jakarta.mail:jakarta.mail-api に切り替えて版を上げたら、コンパイルが次のエラーで止まることがあります。

[ERROR] /app/src/main/java/App.java:[1,20] package javax.mail does not exist

コードは変えていません。変わったのは、依存が提供する package の名前です。javax.mail 配下の別のクラスを import していれば、その package 名で同じ形のエラーになります。

直し方の要点を先に

import を jakarta.mail.* に書き換えます。依存の版は 2.x のまま上げておきます。

// 旧:jakarta.mail-api 1.6 系が提供していたのは javax.mail
// import javax.mail.Session;

// 新:2.0.0 以降が提供するのは jakarta.mail
import jakarta.mail.Session;

jakarta.mail-api を 1.6 系に下げると、コンパイルは通ります(実測:1.6.7 で終了コード 0)。 ただしそれは Jakarta EE 8 世代へ 1 つ戻す選択で、Jakarta EE 9 以降へ移る作業ではありません。 Jakarta EE 9+ の世代(Spring Boot 3、Tomcat 10 など)へ進むのが目的なら、2.x のまま import を進めます。 逆に、周辺のライブラリが javax.* 世代のままで、当面それを維持するのなら、1.6 系が整合する選択になります。 どちらを選ぶかは、周辺が要求している名前空間で決まります。

なぜ 2.0.0 以降で出るのか:Maven 座標が先に改名され、package は後から動いた

境界は2つあり、別の版で起きています。

  1. Maven 座標の移行com.sun.mail:javax.mailjakarta.mail:jakarta.mail-api(1.6.3 から)。 groupId も artifactId も jakarta を名乗るようになります。
  2. Java package の移行javax.mailjakarta.mail(2.0.0 から)

この2つは同時に起きていません。 座標が jakarta を名乗り始めた 1.6.3 の時点でも、その jar が提供するクラスは まだ javax.mail パッケージに入っています。名前が先に変わり、中身は 2.0.0 で動きました。

同じ import javax.mail.Session; だけのソースを、依存の座標と版だけ変えてコンパイルした観測点です。

依存結果
com.sun.mail:javax.mail:1.6.2(旧座標)成功(終了コード 0)
jakarta.mail:jakarta.mail-api:1.6.7成功(終了コード 0)
jakarta.mail:jakarta.mail-api:2.0.1package javax.mail does not exist(終了コード 1)
jakarta.mail:jakarta.mail-api:2.1.3package javax.mail does not exist(終了コード 1)

ソースは4回とも同一で、変えたのは依存の座標だけです。この4点は観測点で、package が動く境界そのものは 2.0.0 です (jakarta.mail-api の公開版は 1.6.3〜1.6.8 と 2.0.0 以降で、1.6 系はすべて javax.mail、2.0.0 以降が jakarta.mail を 提供します。検証環境の節を参照)。

この2段構えが、次の形で表面化します。

  • 座標を替えた時点では通り、あとで版を上げたときに落ちる。 com.sun.mail:javax.mail から jakarta.mail:jakarta.mail-api の 1.6 系へ替える変更は、import を1行も触らずに通ります。座標は jakarta に なったので移行が済んだように見えますが、package はまだ javax.mail です。落ちるのは、その次に 2.x へ上げたときです。
  • 依存の名前で移行状況を判断すると外れる。 依存ツリーに jakarta.mail:jakarta.mail-api があることは、 jakarta.mail パッケージを使えることを意味しません。版まで見る必要があります。

旧座標 com.sun.mail:javax.mail:1.6.2 でも通るのは、こちらも中身が javax.mail だからです。 「座標が jakarta を名乗っているか」ではなく「その版の中身がどちらの名前空間か」だけが結果を決めます。

既存の JAXB のエラーとは別物です

javax.* が見つからないエラーは JDK 11 の JAXB 削除でも出ますが、別の機構です。混同すると直し方を間違えます。

NoClassDefFoundError: javax/xml/bind/JAXBExceptionこの記事
何が変わったかJDK が JAXB の同梱をやめた(Java 11 / JEP 320)ライブラリが package 名を改名した(Jakarta EE 9)
いつ落ちるか実行時NoClassDefFoundErrorコンパイル時package ... does not exist
直し方依存を足すimport を書き換える

javax.mail を提供する古い jar を足せば、コンパイル自体は通ります。ただしそれは 1.6 系に下げるのと同じで、 Jakarta EE 9 へ移る作業にはなりません。

「Maven 座標が先に改名される」という仕組み自体は、JAXB でも同じように起きていますjakarta.xml.bind-api は 2.3.x の中身が javax.xml.bind、3.x 以降が jakarta.xml.bind)。 それでも勧めている向きが逆に見えるのは、選ぶ基準が「周辺が要求している名前空間」だからです。

  • JAXB の記事が 2.3.x を勧めるのは、周辺が javax 世代のままだから。 JDK から消えた API を戻すのが目的で、 import javax.xml.bind.* のコードを動かしたい。だから javax を含む版を選びます。
  • こちらで 2.x のまま import を進めるのは、周辺を Jakarta EE 9+ へ進める前提だから。 周辺が javax 世代なら、 上に書いたとおり 1.6 系が整合します。

つまりどちらも「周辺が要求する名前空間に、その版の中身を合わせる」という同じ基準で選んでいます。 向きが違って見えるのは、周辺の世代が違うからです。

切り分け(うまくいかないとき)

  • package javax.persistence does not exist が出ている:JPA 側の同じ世代ずれです。 package javax.persistence does not exist の直し方(Spring Boot 3) を参照してください。
  • コンパイルは通るのに、実行時に NoClassDefFoundError: javax/servlet/... が出る:自分のコードではなく、 依存しているライブラリが古い名前空間を要求しています。 NoClassDefFoundError: javax/servlet/Filter の直し方 を参照してください。
  • javax.mail 配下の別の package でも同じエラーが出るjavax.mail.internet などの import が残っていないか 確認します。名前空間は配下ごと動くので、javax.mail.internet.MimeMessagejakarta.mail.internet.MimeMessage になります(Jakarta EE 9 の名前空間変更に沿った仕様で、 この記事の実測は javax.mail.Session の1点です)。
  • 依存ツリーに両方の世代が入っているmvn dependency:treejakarta.mail:jakarta.mail-apicom.sun.mail:javax.mail が同居していないかを見ます。同居が問題になるのは、両方が javax.mail を提供するとき (=新座標の 1.6 系と旧座標の組み合わせ)で、このとき同じ完全修飾名が2つの jar から供給されます。 2.x と旧座標の組み合わせでは、提供する package が jakarta.mailjavax.mail で別々なので、 どちらかがもう一方を隠すことはありません(この場合の問題は、2つの世代を同時に抱えている構成そのものです)。

検証環境

  • eclipse-temurin:17-jdk の中で Maven を実行(依存の取得にのみネットワークを使用)
  • 再現:import javax.mail.Session; のみのクラスを jakarta.mail:jakarta.mail-api:2.0.1 でコンパイルすると、 package javax.mail does not exist で終了コード 1
  • 修正:import を jakarta.mail.Session に変更すると、同じ依存(2.0.1)のまま終了コード 0・シグネチャ消滅

再現から修正までは errfix の検証ハーネスが機械的に確認しています。reproduce と fix の差は import 文の1行だけで、 pom.xml の依存の版は同一です。コンパイル結果(旧座標 1.6.2 と新座標 1.6.7 で通り、2.0.1 と 2.1.3 で落ちる)は、 同一ソースを依存の座標だけ変えて実測しました。

package が動く版を確定させるため、jar の中身も直接確認しました——jakarta.mail:jakarta.mail-api1.6.3(新座標の最初の版)と 1.6.8(1.6 系の最後の版)は javax/mail/ のエントリだけを含み (javax/mail/Session.class を含む)、2.0.0jakarta/mail/ のエントリだけを含んで javax/mail/ を 1つも含みません。したがって package の境界は 2.0.0 です。座標の境界(com.sun.mail:javax.mailjakarta.mail:jakarta.mail-api)は 1.6.3 で、この2つは別の版で起きています(各結果はケースの verification/probes.txt に記録)。package が javax.mail から jakarta.mail へ移ったこと自体は、 Jakarta EE 9 以降の名前空間変更に沿っています。

検証(machine-verified)

この修正は eclipse-temurin:17-jdk のバージョン固定コンテナ内で再現し、修正後に package javax.mail does not exist のシグネチャが消えることを機械で確認しています。

verify — run-case.mjs
$ node run-case.mjs jvm/jakarta-mail-namespace-version
● reproduce package javax.mail does not exist present ✓
● apply fix exit 0
● re-run package javax.mail does not exist gone ✓
PASS verified · eclipse-temurin:17-jdk · signature gone

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