Poetry を 2 系に上げると、poetry export が無いと言われます。
The requested command export does not exist.
Documentation: https://python-poetry.org/docs/cli/
同じ原因でもう 1 つの文言があります。どちらが出るかは Poetry の版で決まります。
The command "export" does not exist.
| Poetry | poetry export(プラグイン無し) |
|---|---|
| 1.8.5 | 動く。poetry-plugin-export will not be installed by default in a future version of Poetry. と警告するだけ |
| 2.0.0 / 2.0.1 | The command "export" does not exist. |
| 2.1.0 / 2.4.1 | The requested command export does not exist. |
どちらが出るかは poetry --version で決まります。検索して出てくる記事の文言が自分と違っていても、扱っているのは同じ症状です。
Poetry 2.0.0 で export が本体から外れ、poetry-plugin-export というプラグインになりました。 1.8.5 の警告はその予告です。プラグインを入れれば戻ります。
poetry self add poetry-plugin-export
ここまでで直った人は、以下を読む必要はありません。 この記事が扱うのは、プラグインを入れる宣言を pyproject.toml に書いたのに、同じエラーが消えない場合です。
まず、どちらの状態かを分ける
pyproject.toml に次の宣言があるかを見てください。
[tool.poetry.requires-plugins]
poetry-plugin-export = ">=1.8"
これは Poetry 2.x のドキュメントが案内している書き方で、poetry install の時点でプラグインを取りに行きます。この宣言があって、しかも poetry export が上のエラーで止まるなら、この記事の症状です。
poetry install は終了コード 0 で終わり、プラグインを入れたと出力します。
$ poetry install --no-root
Ensuring that the Poetry plugins required by the project are available...
The following Poetry plugins are required by the project but are not installed in Poetry's environment:
- poetry-plugin-export (>=1.8)
Installing Poetry plugins only for the current project...
- Installing poetry-plugin-export (1.10.0)
Writing lock file
エラーも警告も出ません。次に poetry export を叩いた時点で初めて、入っていないことが分かります。
直し方の要点を先に
ビルドの段階で、Poetry を実行するのと同じ経路にプラグインを入れます。 Dockerfile なら 1 行です。
RUN pip install poetry==2.4.1 \
&& poetry self add poetry-plugin-export
USER app
pip install poetry-plugin-export でも同じ結果になります(Poetry と同じ Python に入れる場合)。どちらも、USER を切り替えたあとの poetry export が終了コード 0 になります。
RUN pip install poetry==2.4.1 poetry-plugin-export
USER app
pyproject.toml の requires-plugins は消さなくて構いません。ただし、この 2 つはその宣言を働かせているのではなく、別経路でプラグインを入れて結果を合わせています。 宣言のほうを働かせたい場合は次の節です。
宣言をそのまま働かせる場合
requires-plugins の宣言を版の正本として使い続けるなら、Poetry を仮想環境に入れて、その仮想環境ごと実行ユーザーへ渡します。 下はイメージの中で自分が作った仮想環境に対する操作です。ホストに入れた Poetry へ chown を打たないでください(共有マシンなら他のユーザーの実行環境の所有者を書き換えます)。ホストで宣言を働かせたいなら、最初からそのユーザーで仮想環境を作ります。
RUN python -m venv /opt/poetry-venv \
&& /opt/poetry-venv/bin/pip install poetry==2.4.1 \
&& chown -R app /opt/poetry-venv
ENV PATH="/opt/poetry-venv/bin:$PATH"
USER app
所有者を渡すのが一番手っ取り早い形です。仮想環境に入れただけでは直りません——root が作った仮想環境を別のユーザーが実行すると、システムの Python に入れた場合と同じく poetry export が終了コード 1 になります(次節の表)。
イメージを変えられない場合
USER が固定された CI ランナーや、配布された共有ベースイメージのように、ビルド段に手を入れられない場合は、実行時の環境変数 1 つで通ります。
ENV PYTHONNOUSERSITE=1
docker run -e PYTHONNOUSERSITE=1 ... でも同じです。効かせる相手は poetry install を走らせる側で、これを立てると宣言どおり .poetry/plugins にプラグインが入ります。理由は後述——Poetry の退避先がユーザーサイトなので、それを止めると退避そのものが起きません。 ユーザーサイトを止めるので、pip install --user で入れたものに依存している構成では影響を確認してください。
なぜ Poetry 2.1.0 で変わったのか
同じ pyproject.toml・同じ手順で、Poetry の版だけを変えた結果です。実行ユーザーは root 以外に固定しています。
| Poetry | poetry install のログ | poetry export |
|---|---|---|
| 2.0.0 | Installing poetry-plugin-export (1.9.0) | 通る |
| 2.0.1 | 同上 | 通る |
| 2.1.0 | Installing poetry-plugin-export (1.10.0) | 失敗 |
| 2.1.2 | 同上 | 失敗 |
| 2.4.1 | 同上 | 失敗 |
境界は 2.0.1 と 2.1.0 のあいだです。 PyPI にこの 2 つの中間のリリースは無いので、境界はここで確定します。2.1.0〜2.4.1 のあいだで測ったのは 2.1.0 / 2.1.2 / 2.4.1 の 3 点で、2.4.1 は執筆時点の最新版です。
変わったのは requires-plugins の側ではありません。 プラグインをプロジェクト配下の .poetry/plugins へ入れる方式も、そのために purelib を上書きする実装も、2.0.0 の時点で既にあります(2.0.0 の plugin_manager.py に PATH = Path(".poetry") / "plugins" と poetry_env.paths["purelib"] = str(self._path) があります)。それでも 2.0.x は通ります。
2.1.0 が足したのは Env.scheme_dict です。CHANGELOG にはこう書かれています。
Fix an issue where installation failed with a permission error when using the system
environment as a user without write access to system site packages (#9014).
システムの site-packages に書けないユーザーを救済する処理で、書けないパスを $HOME/.local の下へ読み替えます。この読み替えが、requires-plugins が上書き済みの purelib と噛み合いません。
なぜ書き込み権限で決まるのか
Env.scheme_dict は、インストール先の各パス(purelib / platlib / headers / scripts / data)を順に見て、1 つでも書けなければ全部を $HOME/.local の下へ退避します。判定は所有者ではなく、そのディレクトリに一時ファイルを作れるかどうかです。
ここで効いてくるのが、requires-plugins による上書きです。purelib と platlib は既に .poetry/plugins(プロジェクト配下)へ差し替わっているので、最初から書けます。残るのは scripts(/usr/local/bin)と data(/usr/local)で、判定を決めているのはこの 2 つだけです。
| Poetry の置き場所 | 実行ユーザー | .poetry/plugins に実体 | poetry export |
|---|---|---|---|
システムの Python(pip install poetry) | root | ある | 通る |
| システムの Python | 別ユーザー | 無い | 失敗 |
システムの Python + chmod a+w /usr/local /usr/local/bin | 別ユーザー | ある | 通る |
| 仮想環境(root が所有) | 別ユーザー | 無い | 失敗 |
仮想環境 + chmod a+w(所有者は root のまま) | 別ユーザー | ある | 通る |
仮想環境(実行ユーザーへ chown) | 別ユーザー | ある | 通る |
site-packages の書き込み可否は関係しません。 上の 3 行目は site-packages を root 所有の 755 のまま触っていません。requires-plugins の入れ先は既に .poetry/plugins へ差し替わっているので、site-packages はこの判定に使われないからです。chmod -R a+w を site-packages に当てても、エラーは 1 文字も変わりません。
Dockerfile で pip install poetry してから USER を切り替える形は、この表の 2 行目にそのまま当たります。コンテナだけの話ではありません——sudo pip install poetry した開発機で自分のユーザーとして poetry install を打つ構成も同じ条件です(上流 issue にも macOS からの報告があります)。
入ったはずのプラグインはどこにあるか
失敗した側の .poetry/plugins には、設定とロックファイルしか残りません。
$ find .poetry
.poetry
.poetry/plugins
.poetry/plugins/config.toml
.poetry/plugins/poetry.lock
.poetry/.gitignore
実体はホームの下にあります。$HOME/.local がパス成分ごとに前置された名前のディレクトリが生えます。
$ ls -a $HOME
.bash_logout .bashrc .cache .localhome .localusr .profile p
$ ls "$HOME/.localhome/home/app/.localapp/home/app/.localp/home/app/.local.poetry/home/app/.localplugins/poetry_plugin_export"
__init__.py command.py exporter.py plugins.py
ls -a でホームに .local で始まる見慣れないディレクトリが並んでいたら、この症状です。
この名前は、退避先を組み立てる 1 行から出ています。 Env.scheme_dict は、退避するときの接頭辞を「scripts と purelib の共通パス」として求めます。ところが purelib は requires-plugins によって .poetry/plugins(プロジェクト配下)へ差し替わっているので、/usr/local/bin との共通パスは / にしかなりません。その後の置換が文字列置換なので、パス区切りの / が全部 $HOME/.local に置き換わります。
自分の環境で 1 回で確かめられます。
$ python -c '
from poetry.utils.env import EnvManager
e = EnvManager.get_system_env(naive=True)
e.set_paths(purelib="/work/.poetry/plugins", platlib="/work/.poetry/plugins")
print(e.scheme_dict["purelib"])'
/home/app/.localwork/home/app/.local.poetry/home/app/.localplugins
仮想環境でも条件は変わりません。 Poetry がプラグイン導入時に使う環境は EnvManager.get_system_env(naive=True) で作られ、これは仮想環境の中で動かしても is_venv() が False になります。退避するかどうかの判定に使われるのも site.ENABLE_USER_SITE ではなく site.check_enableusersite() で、こちらは仮想環境でも True を返します(python -c 'import site; print(site.ENABLE_USER_SITE, site.check_enableusersite())' で False True と出ます)。引き金は 1 つで、確かめる場所は上の 2 パスだけです。
上流の issue(python-poetry/poetry#10341)は open で、修正 PR(#10998「Fix installation of project plugins if Poetry’s environment is not writable」)も 2026-07-30 から open のままです。これがリリースに入れば、この症状はその版から消えます。
切り分け
requires-pluginsを書いていない → この記事の症状ではありません。poetry self add poetry-plugin-exportで入ります。Poetry を pipx で入れているなら、Poetry のドキュメントはpipx inject poetry poetry-plugin-exportを案内しています(この経路は実測していません)。poetry installは成功しているのにpoetry exportだけ落ちる → この記事の症状です。まずls -a $HOMEを見てください。- 手元では通るのに CI・コンテナだけ落ちる →
DockerfileにUSERがあるかを見てください。ビルド段でpip install poetryし、実行段で別ユーザーに切り替えている構成が該当します。sudo pip install poetryした開発機でも同じ条件になります。 poetry self show pluginsの出力で判定したい → 成功している側ではpoetry-plugin-export (1.10.0) … 1 application pluginが出ます(chmodで通した構成で実測)。ただし失敗側でも空とは限らないので、確実なのは.poetry/pluginsの中身を見ることです。The command "export" does not exist.のほうが出ている → Poetry 2.0.0 / 2.0.1 の文言です(2.1.0 でThe requested command export does not exist.に変わりました)。症状は同じ「コマンドが無い」ですが、原因は別です——2.0.x はプラグインを入れていないだけで、requires-pluginsを書けば動きます(2.0.1 で実測)。この記事の後半は当たりません。export以外のコマンドが無いと言われる → 2.0.0 で分離されたのはexportだけではありません。同じ形でpoetry self addするか、この記事の構成の問題を疑ってください(本記事が実測したのはexportだけです)。
検証環境
- image:
python:3.13-slim(digest 固定sha256:ffb752e1...)、ネットワーク有り(PyPI から取得するため) - 再現:
[tool.poetry.requires-plugins]を書いたpyproject.tomlを、root が作った仮想環境の Poetry 2.4.1 で、別ユーザーからpoetry install --no-root→ 成功。続けてpoetry export --format requirements.txt→The requested command export does not exist.で終了コード 1 - 修正:同じ Poetry・同じ
pyproject.tomlのまま、仮想環境を実行ユーザーへchown→ 同じ 2 コマンドが終了コード 0 になり、idna==3.10が出力される
再現から修正までは errfix の検証ハーネスが機械的に確認しています。reproduce と fix の差は chown の 1 行だけで、pyproject.toml は 2 つのディレクトリで byte 単位に同一です。
版の表(2.0.0 / 2.0.1 / 2.1.0 / 2.1.2 / 2.4.1)、文言の表(1.8.5 の警告と、2.0.x と 2.1.0 以降で変わるエラー文)、置き場所と権限の表、site-packages を書き込み可にしても変わらないこと、chmod a+w /usr/local /usr/local/bin の 2 つだけで通ること(site-packages は root 所有 755 のまま)、仮想環境を chown せず chmod だけで通ること、PYTHONNOUSERSITE=1 で通ること、poetry self add と pip install poetry-plugin-export をビルド段で打てば別ユーザーでも通ること、poetry self show plugins が成功側でプラグインを表示すること、失敗時の .poetry/plugins の中身と $HOME に生えるディレクトリ名は、いずれも同じイメージで実測しています。候補ごとにコンテナを分けて測っています——同じコンテナで続けて測ると、前の手当てが次の測定を通してしまいます。
上に貼った $HOME の中身と長いパスは、プロジェクトを /home/app/p に置いて測ったときの出力です(検証ケースは /work に置いているので、.localwork のような名前になります)。
requires-plugins がプロジェクト配下へ入れる仕様と purelib を上書きする実装、退避先を組み立てる Env.scheme_dict は、Poetry 2.4.1 と 2.0.0 の両方の site-packages を直接読んで比べました。2.1.0 の CHANGELOG と PR #9014、issue #10341、PR #10998 は GitHub で確認しています。
pipx で入れた Poetry、Poetry 公式インストーラで入れた Poetry、export 以外の分離コマンド、Windows は実測していません。 ただし pipx と公式インストーラはどちらも Poetry を実行ユーザーの所有物として置くので、上の条件からは外れると考えられます(測ってはいません)。