CLAUDE LABEN
FORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられていますFORK — Claude Code 2.1.212で/forkの挙動が変わりました。会話を新しいバックグラウンドセッションへ複製し、作業を続けたまま並走できます。従来のセッション内サブエージェントは/subtaskに移りましたLIMITS — WebSearchの呼び出しがセッション単位で既定200回に制限されました。サブエージェントの起動も既定200回が上限で、暴走した検索・委譲のループを止められますMCPBG — 2分を超えるMCPツール呼び出しは自動的にバックグラウンドへ移り、セッションが固まらなくなりました。しきい値はCLAUDE_CODE_MCP_AUTO_BACKGROUND_MSで調整できますPLANFIX — プランモードがtouchやrmといったファイルを変更するBashコマンドを、許可プロンプトもcanUseToolコールバックも通さずに実行してしまう不具合が修正されましたSONNET5 — Claude Sonnet 5は導入価格として入力100万トークンあたり2ドル、出力10ドルで提供中です。8月31日を過ぎると3ドルと15ドルに戻りますIPO — Anthropicが早ければ10月の株式公開を視野に、引受銀行が投資家との面談を組み始めたと報じられています
記事一覧/Claude Code
Claude Code/2026-05-27中級

Claude Code Bash ツールで /tmp が Permission denied になる時の対処 — $HOME/repos フォールバックパターン

Claude Code のサンドボックス Bash で git clone が `Permission denied` で止まる症状の原因と、$HOME/repos へ自動退避させる実装パターンを、4サイト並行運用での実体験とともに整理します。

Claude Code198Bash4サンドボックス5git16トラブルシューティング37

朝のスケジュールタスクのログを開くと、fatal: could not create work tree dir '/tmp/repos/claudelab.net': Permission denied の1行で止まっていることがあります。実行ユーザーは確かに動いている、Git トークンも有効、ディスク容量も残っている。けれども /tmp/repos のサブディレクトリが前回セッションの後始末で nobody:nogroup 所有のまま残っていて、新しいセッションのユーザーでは書き込みも削除もできない、という状態でした。

私は Dolice Labs として Claude Lab・Gemini Lab・Antigravity Lab・Rork Lab の4サイトを Claude Code のスケジュールタスクで自動更新しています。各サイト毎日4本の記事を別ユーザー権限のサンドボックスで生成しているため、この /tmp まわりの権限事故は半年で十数回ぶつかってきました。今回は再現の原因と、無人運用でも止まらない $HOME/repos フォールバックの最小実装をまとめます。

症状とログ

代表的なログはこの形です。

$ git clone --depth 1 https://...@github.com/masakihirokawa/claudelab.net.git /tmp/repos/claudelab.net
Cloning into '/tmp/repos/claudelab.net'...
fatal: could not create work tree dir '/tmp/repos/claudelab.net': Permission denied

加えて、ls -la /tmp/repos/ でディレクトリ所有者が nobody:nogroup になっているのが特徴です。

$ ls -la /tmp/repos/
drwxr-xr-x  3 nobody nogroup 4096 May 25 07:34 .
drwxrwxrwt 16 nobody nogroup 4096 May 27 01:34 ..
drwxr-xr-x  7 nobody nogroup 4096 May 25 07:34 antigravitylab.net

このときの私の実行ユーザーは nobody ではないので、既存ディレクトリの中に新しいワークツリーを作れません。rm -rf も同じ理由で弾かれます。

なぜ起きるのか

サンドボックス VM では、セッションごとに実行ユーザーが切り替わる構成があります。前回セッションが /tmp/repos/{site} を作って nobody:nogroup で残したまま終了し、次のセッションが別ユーザーで起動すると、その所有権を引き継げません。/tmp 自体は sticky bit 付き (drwxrwxrwt) なので新規ディレクトリは作れますが、既に存在する他人所有のサブツリーには手が出せない、というのが本質です。

ENOSPC(ディスク不足)と紛らわしい挙動ですが、エラーメッセージが No space left on devicePermission denied かで切り分けられます。前者はキャッシュ削除、後者は書き込み先の付け替えで解決します。

最小フォールバック実装

私が4サイトの SKILL.md に組み込んでいるのは、/tmp/repos が書き込めなければ $HOME/repos に退避する3行です。

WORK="/tmp/repos/${SITE}"
 
# /tmp/repos が書けるか実地で検証(権限と容量の両方)
if ! mkdir -p "$WORK" 2>/dev/null; then
  WORK="$HOME/repos/${SITE}"
  mkdir -p "$WORK"
fi
 
echo "WORK=$WORK"

mkdir -p を実地に叩いて成否で判定するのが要点です。test -w /tmp/repos だけだと「ディレクトリ自体は書ける、けれど中の claudelab.net/ には書けない」というケースを取りこぼします。私は半年前にこの判定漏れでスケジュールタスクが2日連続で空振りしました。

clone と pull の分岐

フォールバック先を決めた後は、既存リポジトリがあるかどうかで clonepull --rebase を分けます。

WS="$(ls -d /sessions/*/mnt/Dolice\ Labs 2>/dev/null | head -1)"
TOKEN=$(grep -A1 "^${SITE_LABEL}" "${WS}/_documents/_github_tokens/github_tokens.txt" \
        | tail -1 | tr -d '[:space:]')
 
if [ -d "$WORK/.git" ]; then
  cd "$WORK" && git pull --rebase origin main 2>&1 | tail -5
else
  git clone --depth 1 \
    "https://${TOKEN}@github.com/masakihirokawa/${SITE}.git" "$WORK" \
    2>&1 | tail -5
fi

--depth 1 は記事追加用途であれば充分です。Cloudflare の CI 側で prebuild フックが generate-content.mjs を走らせる設計なので、ローカルで npm install や Next.js のビルドを試す必要はありません。記事を1本書いて push するだけなら、リポジトリの実体は数十 MB に収まります。

ディスク不足との合わせ技

$HOME/repos に逃がしても、ホームディレクトリ側が満杯であれば同じことです。容量チェックを前段に置き、500MB を切ったら他サイトの作業用リポジトリを掃除しておくのが安全策でした。

FREE_MB=$(df "$HOME" --output=avail -m 2>/dev/null | tail -1 | tr -d ' ')
if [ "${FREE_MB:-0}" -lt 500 ]; then
  for OTHER in "$HOME/repos"/*; do
    [ "$OTHER" != "$WORK" ] && rm -rf "$OTHER" 2>/dev/null
  done
fi

4サイトを並行で動かしていると、午前2時のプレミアム枠と午後3時の通常記事枠で同じ VM が再利用されるケースがあります。前のタスクが残した node_modules で 800MB 食っていたこともあり、容量解放のロジックを最初に通すだけで全体の安定性がだいぶ変わりました。

予防 — SKILL.md にフォールバックを直接書いておく

スケジュールタスクの本体プロンプトと、各サイトの _skills/{site}/SKILL.md の両方に、フォールバック処理を明示しておくのが現実的でした。CLAUDE.md だけに書いておいても、スケジュール実行時のコンテキストには SKILL.md と本体プロンプトしか届かない場面があるためです。

# _skills/claudelab/SKILL.md の Step 0 に貼ってある実装
WORK="/tmp/repos/claudelab.net"
mkdir -p "$WORK" 2>/dev/null || WORK="$HOME/repos/claudelab.net"
mkdir -p "$WORK"

|| で短く書く版は、シェルスクリプトに直接書く時には読みやすいです。CLAUDE.md には事象と背景、SKILL.md には実装、本体プロンプトには「失敗時は自動リカバリ」と書き分けると、後から読み返した時にも経緯が追えます。

切り分けチェックリスト

事故ったときに最初に確かめている順番です。

  1. ls -la /tmp/repos/ で対象サブディレクトリの所有者を確認する
  2. whoamiid で現在の実行ユーザーを確認する
  3. df -h /tmpdf -h $HOME で両方の空き容量を確認する
  4. mkdir -p "$WORK" && touch "$WORK/.write_test" && rm "$WORK/.write_test" で実書き込みテストを通す
  5. それでも駄目なら WORK=$HOME/repos/${SITE} に切り替えて同じテストを通す

このうち4のステップは、test -w よりも実地検証のほうが副作用込みで判定できるので確実でした。私はここで1度はまって、ストレージドライバが書き込み許可だけ返しておいて実際の書き込みで EPERM を返す事象に当たったことがあります。

なぜ私は4年前からこの仕組みを必要としているか

私が個人開発を始めたのは2014年で、当時から Beautiful HD Wallpapers などの壁紙アプリと癒し系アプリを App Store と Google Play で出し続けてきました。累計で5,000万ダウンロードまで来ているのですが、AdMob 月収を安定させながら現代美術家としての活動も続けるとなると、ブログ運営の手は最大限自動化する以外に道がありませんでした。

2014年〜2019年ごろは手作業で記事を書いていましたが、Claude Code のスケジュールタスクと組み合わせるようになってから、4サイト × 1日4本という分量を無人で回せるようになりました。とはいえサンドボックスの権限事故で1日4本が0本になる失敗は、月初の Stripe メンバーシップ収益にも直結します。Permission denied 1行で2日空いた朝、ログイン中だった iPhone から Apple Watch 越しに通知が飛んできて、吉祥寺のカフェで対応した記憶があります。

それ以来、/tmp を当てにしないことと、フォールバック先での書き込み実地検証は、自動運用の最低保証ラインとして外せなくなりました。

4サイト横展開の小ネタ

4つの SKILL.md を同じパターンで書き換えるとき、サイト名だけを差し替える前提でテンプレートを共通化しています。

# 共通スニペット(4サイト全てに同じ形で組み込み)
SITE_REPO="$1"   # 例: claudelab.net
WORK="/tmp/repos/${SITE_REPO}"
mkdir -p "$WORK" 2>/dev/null || WORK="$HOME/repos/${SITE_REPO}"
mkdir -p "$WORK"
touch "$WORK/.write_test" 2>/dev/null || {
  echo "write test failed even on fallback: $WORK" >&2
  exit 1
}
rm "$WORK/.write_test"
echo "WORK=$WORK"

.write_test のステップを最後に置いておくと、ストレージドライバが嘘の許可を返す事例も拾えます。私はこれを Antigravity Lab で1度経験してから、必ず touch + rm のセットで検証するようにしました。

自動運用へ落とし込む

4サイト分のスケジュールタスクを Cowork 側から走らせている都合上、エラー時に止めるのではなく自動で次の手を打たせる方針にしました。フォールバック成功時にもログには WORK=$HOME/repos/... と書き出しておくと、後で task_error.log を眺めたときに「あの日のあの記事はホーム側で生成された」と追跡できます。

無人運用で記事を更新し続けるなら、/tmp を当てにしないという前提に切り替えるのが結局のところ一番強い対策でした。$HOME/repos は同じセッション内では確実に書ける場所で、シャットダウン時に消えてもまた clone すればよいだけです。

似たような無人パイプラインを組んでいる方の参考になれば幸いです。お読みいただきありがとうございました。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

Claude Code2026-05-30
git の detected dubious ownership が出てコマンドが止まるときの原因と対処
Claude Code やコンテナ・CI でgitを動かすと突然出る「detected dubious ownership in repository」の原因を整理し、safe.directory設定から所有権の根本対処までを実例で解説します。
Claude Code2026-05-20
Claude Code が『テスト全通過』と報告したのに CI で落ちる — 終了コードを正しく拾うための検証設計
ローカルでは『テスト全通過』と報告された変更が、push 後の GitHub Actions で真っ赤になる。原因の多くは Claude Code 側ではなく、シェルとテストランナーの終了コード解釈にあります。実例ベースで切り分け方を整理しました。
Claude Code2026-05-18
Claude Code の Bash ツールで cd や export が引き継がれない時の対処
Claude Code の Bash ツールで cd や export が次のコマンドに反映されない症状の原因と、実務で使える5つの対処パターンを実例とともに整理しました。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →