Python でファイルを開いたり書き出したりしたときに PermissionError: [Errno 13] Permission denied: ... で止まったら、まずトレースバックの最終行の番号を見ます。同じ PermissionError でも、Mac では [Errno 1] が出ることがあり、直し方が違うからです。
番号が 13 なら、新しくファイルを作ろうとしたときは書き込み先のフォルダ、既存のファイルを読み書きしたときはそのファイルの権限を ls -ld/ls -l で見ます。持ち主が自分なら、足りない1文字だけを chmod u+w や chmod u+r で足せば直ります。持ち主が自分でない場所なら、権限を変えるのではなく書き込み先を自分のフォルダへ移します。
以下は、このサイトの運営に使っているMac(macOS 26.6.2)で、2026年10月19日に実際に打った記録です。自分のファイルを使う実験は /tmp の下に作った一時フォルダで行い、システムの場所へは「書けずに失敗する」ことの確認だけをしています(sudo は使っていません)。ホームフォルダのパスは ~ に置き換えています。
システムの場所へ書こうとすると番号が3種類に分かれた
引数で受け取った場所を open() するだけの小さなスクリプトを用意し、いくつかの場所へ書き込みを試しました。
% cat w.py
import sys
path, mode = sys.argv[1], sys.argv[2]
open(path, mode)
% python3 w.py /usr/bin/itmemo_test.txt w
Traceback (most recent call last):
File "/private/tmp/itmemo142b.fTtIWW/w.py", line 3, in <module>
open(path, mode)
PermissionError: [Errno 1] Operation not permitted: '/usr/bin/itmemo_test.txt'
% python3 w.py /Library/itmemo_test.txt w
Traceback (most recent call last):
File "/private/tmp/itmemo142b.fTtIWW/w.py", line 3, in <module>
open(path, mode)
PermissionError: [Errno 13] Permission denied: '/Library/itmemo_test.txt'
% python3 w.py /itmemo_test.txt w
Traceback (most recent call last):
File "/private/tmp/itmemo142b.fTtIWW/w.py", line 3, in <module>
open(path, mode)
OSError: [Errno 30] Read-only file system: '/itmemo_test.txt'
| 書こうとした場所 | 最終行 | 意味 |
|---|---|---|
/usr/bin | PermissionError: [Errno 1] Operation not permitted | システム整合性保護(SIP)の対象。持ち主や権限の問題ではない |
/System | PermissionError: [Errno 1] Operation not permitted | 同上 |
/Library | PermissionError: [Errno 13] Permission denied | 持ち主が root の普通のフォルダで、自分に w が無い |
/usr/local | PermissionError: [Errno 13] Permission denied | 同上 |
/(起動ディスクの直下) | OSError: [Errno 30] Read-only file system | ディスク自体が読み取り専用。PermissionError ですらない |
トレースバックは下から読みます。
File "…/w.py", line 3, in <module>:どのファイルの何行目で起きたか- その下の行:実際に実行されたコード(ここでは
open(path, mode)) - 最終行:例外の種類、
[Errno 番号]、OS からの説明文、対象のパス。直し方を決めるのはこの行です
5か所とも ls -ld で見ると持ち主は root、権限は同じ drwxr-xr-x でした。つまり ls -l の見た目だけでは 1 と 13 を区別できず、番号を見るしかありません。
% ls -ld / /Library /System /usr/bin /usr/local
drwxr-xr-x 22 root wheel 704 8月 13 11:51 /
drwxr-xr-x 69 root wheel 2208 8月 27 23:32 /Library
drwxr-xr-x@ 10 root wheel 320 8月 13 11:51 /System
drwxr-xr-x 926 root wheel 29632 8月 13 11:51 /usr/bin
drwxr-xr-x 6 root wheel 192 8月 27 23:32 /usr/local
Apple の説明では、SIP が守るのは /System、/usr、/bin、/sbin などで、/Library(ライブラリ)と /usr/local は他社製のアプリやインストーラが書き込める場所として挙げられています(Macのシステム整合性保護について(Apple サポート))。実測の 1 と 13 の分かれ方はこの説明と一致しました。Errno 1 は管理者権限でも変えられない保護なので、chmod を繰り返しても意味がありません。書き込み先を自分のフォルダに変えます。
Python の公式ドキュメントでも、PermissionError は errno の EACCES(13)と EPERM(1)の両方に対応すると書かれています(組み込み例外 PermissionError(Python 公式ドキュメント))。例外の名前が同じなので、番号まで見る必要があります。
自分のファイルで Errno 13 を出してみた
一時フォルダの中で、よくある4つの形を再現しました。
1. フォルダに w が無いところへ新しく作る
% mkdir ro_dir
% echo old > ro_dir/existing.txt
% chmod u-w ro_dir
% ls -ld ro_dir
dr-xr-xr-x 3 me wheel 96 10月 19 00:20 ro_dir
% python3 w.py ro_dir/new.txt w
Traceback (most recent call last):
File "/private/tmp/itmemo142b.fTtIWW/w.py", line 3, in <module>
open(path, mode)
PermissionError: [Errno 13] Permission denied: 'ro_dir/new.txt'
% python3 w.py ro_dir/existing.txt w
% echo $?
0
dr-xr-xr-x は、1文字目の d がフォルダ、次の3文字 r-x が持ち主(自分)の権限です。w が無いので、中に新しいファイルは作れません。一方、すでにある existing.txt の上書きは成功しました。上書きに要るのはファイル自身の w で、フォルダの w はファイルを作ったり消したりするときに見られるからです。「昨日は書けたのに」というときは、ファイル名が変わって新規作成になっていないかを疑います。
2. 読み取り権限の無いファイルを読む
% echo hi > secret.txt
% chmod 000 secret.txt
% ls -l secret.txt
---------- 1 me wheel 3 10月 19 00:21 secret.txt
% python3 w.py secret.txt r
Traceback (most recent call last):
File "/private/tmp/itmemo142b.fTtIWW/w.py", line 3, in <module>
open(path, mode)
PermissionError: [Errno 13] Permission denied: 'secret.txt'
---------- は誰にも何も許していない状態です。自分のファイルでも読めません。
3. 読み取り専用のファイルに書く(でも消せる)
% echo hi > readonly.txt
% chmod 444 readonly.txt
% python3 w.py readonly.txt w
Traceback (most recent call last):
File "/private/tmp/itmemo142b.fTtIWW/w.py", line 3, in <module>
open(path, mode)
PermissionError: [Errno 13] Permission denied: 'readonly.txt'
% rm -f readonly.txt
% ls readonly.txt
ls: readonly.txt: No such file or directory
書き込みは断られますが、削除はできました。1. の裏返しで、削除はフォルダの w で決まるからです。ファイルを読み取り専用にしても「消されない」保護にはなりません。
4. フォルダに x が無い
% mkdir noexec
% echo data > noexec/a.txt
% chmod u-x noexec
% ls -ld noexec
drw-r-xr-x 3 me wheel 96 10月 19 00:21 noexec
% python3 -c "import os; print(os.listdir(\"noexec\"))"
['a.txt']
% python3 w.py noexec/a.txt r
Traceback (most recent call last):
File "/private/tmp/itmemo142b.fTtIWW/w.py", line 3, in <module>
open(path, mode)
PermissionError: [Errno 13] Permission denied: 'noexec/a.txt'
フォルダの r があるので中の名前の一覧は取れますが、x(中へ入る権限)が無いので、中のファイルは開けません。drw-r-xr-x のように、グループやその他に x があっても、持ち主である自分には持ち主の欄(rw-)が適用されます。
ついでに、フォルダそのものを open() すると番号は 21 でした。IsADirectoryError: [Errno 21] Is a directory: 'ro_dir' は権限ではなく、ファイル名を付け忘れているときの表示です。
直し方:足りない1文字だけ足す
% chmod u+w ro_dir
% python3 w.py ro_dir/new.txt w
% echo $?
0
% chmod u+r secret.txt
% ls -l secret.txt
-r-------- 1 me wheel 3 10月 19 00:21 secret.txt
% python3 w.py secret.txt r
% echo $?
0
u+w は「持ち主(u)に w を足す」という意味です。chmod 777 のように全員に全部を許す必要はありません。これが使えるのは ls -l の3列目(持ち主)が自分のときだけです。root など自分以外が持ち主の場所は、権限を変えずに書き込み先を変えます。
相対パスで、思っていない場所に書いていた
エラーのパスが 'out.txt' のように短いと、どこに書こうとしたのか分かりません。相対パスの基準はスクリプトの場所ではなく、実行したときにいたフォルダです。同じスクリプトを2か所から実行しました。
% cat proj/rel.py
import os
print("cwd :", os.getcwd())
print("script :", os.path.dirname(os.path.abspath(__file__)))
print("out.txt :", os.path.abspath("out.txt"))
open("out.txt", "w").write("x")
% cd proj
% python3 rel.py
cwd : /private/tmp/itmemo142b.fTtIWW/proj
script : /private/tmp/itmemo142b.fTtIWW/proj
out.txt : /private/tmp/itmemo142b.fTtIWW/proj/out.txt
% cd ../ro_dir
% python3 ../proj/rel.py
cwd : /private/tmp/itmemo142b.fTtIWW/ro_dir
script : /private/tmp/itmemo142b.fTtIWW/proj
out.txt : /private/tmp/itmemo142b.fTtIWW/ro_dir/out.txt
Traceback (most recent call last):
File "/private/tmp/itmemo142b.fTtIWW/ro_dir/../proj/rel.py", line 5, in <module>
open("out.txt", "w").write("x")
PermissionError: [Errno 13] Permission denied: 'out.txt'
2回目は ro_dir(w が無いフォルダ)にいたので、out.txt はそこに作られようとして失敗しています。スクリプトと同じ場所に書きたいなら、__file__ から場所を組み立てます。
% cat ../proj/rel2.py
import os
here = os.path.dirname(os.path.abspath(__file__))
out = os.path.join(here, "out2.txt")
open(out, "w").write("x")
print("wrote:", out)
% python3 ../proj/rel2.py
wrote: /private/tmp/itmemo142b.fTtIWW/proj/out2.txt
ファイルが見つからない側(FileNotFoundError)で同じことが起きる例はFileNotFoundError が出る原因とパス確認方法にまとめています。
pip install で止まるのは、権限ではないことが多い
pip install が止まったときに「権限の問題だ」と思って sudo を付けたくなりますが、このMacの Homebrew の Python 3.12 で出たのは別のエラーでした。
% python3.12 -m pip install six
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try brew install
xyz, where xyz is the package you are trying to
install.
If you wish to install a Python library that isn't in Homebrew,
use a virtual environment:
python3 -m venv path/to/venv
source path/to/venv/bin/activate
python3 -m pip install xyz
(中略)
note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
% echo $?
1
1行目の externally-managed-environment は「この Python は Homebrew が管理しているので、pip で直接いじらせない」という意味で、ファイルの権限とは関係ありません。PEP 668 という仕様で、Python の標準ライブラリのフォルダに EXTERNALLY-MANAGED という印のファイルがあると、仮想環境の外での pip install が止められます(PEP 668)。メッセージ自体が、仮想環境を作るように案内しています。
ただ、このMacでは「Mac の Python なら必ずこれが出る」わけではありませんでした。手元の4つの python3 について、その印のファイルがあるかを調べた結果です。
$ for py in ~/opt/anaconda3/bin/python3 /usr/bin/python3 /opt/homebrew/bin/python3.11 /opt/homebrew/bin/python3.12; do f=$($py -c "import sysconfig,os; p=os.path.join(sysconfig.get_path(\"stdlib\"),\"EXTERNALLY-MANAGED\"); print(p if os.path.exists(p) else \"(none)\")"); echo "$py -> $($py --version 2>&1) -> $f"; done
~/opt/anaconda3/bin/python3 -> Python 3.9.7 -> (none)
/usr/bin/python3 -> Python 3.9.6 -> (none)
/opt/homebrew/bin/python3.11 -> Python 3.11.14 -> (none)
/opt/homebrew/bin/python3.12 -> Python 3.12.13 -> /opt/homebrew/opt/python@3.12/Frameworks/Python.framework/Versions/3.12/lib/python3.12/EXTERNALLY-MANAGED
止められるのは Homebrew の 3.12 だけで、同じ Homebrew の 3.11 には印がありませんでした(3.11 の pip に --dry-run を付けて試すと止められず、six はすでに Homebrew 3.11 の site-packages に入っていると表示されました。印が無い理由は確認できていません)。PATH の先頭にいる Anaconda の python3 にも印はありません。つまりこのMacで何も考えずに pip install すると、エラーにならずに Anaconda 本体へ入る形になっています(python3 -m pip --version は Anaconda の pip を指していました。実際に Anaconda へは入れていません)。エラーが出ないことは、正しい場所に入ったことを意味しません。
どの場合でも、プロジェクトごとに仮想環境を作ってその中に入れるのが一番安全でした。
% python3.12 -m venv venv
% source venv/bin/activate
((venv) ) % python -m pip install -q --disable-pip-version-check six
((venv) ) % python -c "import six; print(six.__file__)"
/private/tmp/itmemo142b.fTtIWW/venv/lib/python3.12/site-packages/six.py
six.py の場所が venv の中になっていれば成功です。((venv) ) という二重かっこはこのMacの Homebrew 3.12 製 venv の表示のくせです。有効化のつまずきはvenv が有効化できないときの記事に書きました。
sudo pip install を使わない理由
今回 sudo は実行していないので、ここは上の実験からの説明です。sudo で動かした pip や Python が作るファイルは持ち主が root になります。上の「持ち主が自分でない場所は Errno 13 になる」とおり、あとで自分のユーザーで上書きや削除をしようとすると、今度は自分が書けなくなります。Homebrew 3.12 のように印がある Python では、メッセージにあるとおり --break-system-packages を付けるか pip.conf を書き換えないかぎり止められる仕組みで、メッセージ自身が「Python のインストールや OS を壊すおそれがある」と書いています。
今回は再現できなかったもの
macOS には、デスクトップや書類フォルダなどへのアクセスをアプリごとに許可する仕組みもあり、許可の無いアプリから触ると Operation not permitted(Errno 1)になることがあるとされています。ただ、今回使った環境のターミナルはアクセスが許可されていたため、~/Desktop などの中の存在しないファイルを開いても FileNotFoundError(Errno 2)になるだけで、Errno 1 は再現できませんでした。自分のホームの中で Errno 1 が出た場合は、この仕組みを疑う余地があります。
ターミナルで ./script.py を打ったときの zsh: permission denied は、Python が起動する前にシェルが出すもので別物です。そちらはzsh: permission denied の記事で扱っています。
検証した環境
この記事の調査・再現・執筆は、運営者の指示のもとでAIエージェント(AnthropicのClaude)が行い、公開するかどうかは運営者が決めています。載せた出力は、そのAIエージェントがこのMacで実際にコマンドを実行して得たものです。
- macOS 26.6.2(Apple シリコン)、SIP 有効(
csrutil statusで確認) - zsh 5.9
python3:~/opt/anaconda3/bin/python3(Python 3.9.7、PATH の先頭)/usr/bin/python3:Python 3.9.6(Xcode Command Line Tools)/opt/homebrew/bin/python3.11:Python 3.11.14、/opt/homebrew/bin/python3.12:Python 3.12.13(Homebrew)- 実施日:2026年10月19日。自分のファイルの実験は
/tmpの下の一時フォルダで実行
コメントを残す