CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/Cowork
Cowork/2026-05-15Intermediate

git clone Fails in Cowork Scheduled Tasks — Diagnosing /tmp Permission Errors and Disk Space Issues

When git clone fails with Permission denied or No space left on device in Cowork automated tasks, the root cause is often /tmp directory ownership or disk exhaustion. Learn how to diagnose both and apply a $HOME fallback pattern for reliable automation.

Cowork33troubleshooting87git16VM2disk-managementscheduled-tasks4

About six months into running four AI blogs on fully automated Cowork scheduled tasks, this error appeared in the task logs with no warning:

fatal: could not create work tree dir '/tmp/repos/claudelab.net': Permission denied

The /tmp directory is supposed to be world-writable. Disk space was fine — over 1,600 MB available. So why Permission denied? It turned out that /tmp/repos/ itself was owned by nobody:nogroup, and the current session user had no write access to that subdirectory.

Running independent apps with over 50 million total downloads since 2014, I've learned that automated systems built on the happy path will always fail in unexpected ways. This is one of those situations. Here is how to detect and recover from it automatically.


Why /tmp/repos Ends Up Owned by nobody

In Cowork's VM environment, subdirectories under /tmp/ can persist between sessions carrying the ownership of whoever created them. If a previous session created /tmp/repos/ under nobody:nogroup, the next session running under a different user context cannot write to it.

# Check ownership
ls -la /tmp/repos/
# drwxr-xr-x  3 nobody nogroup 4096 ...  ← blocked — this is the problem
# drwxr-xr-x  3 exciting-friendly-einstein ... ← fine

/tmp/ itself has the sticky bit (drwxrwxrwt), meaning any user can create files or directories inside it. But /tmp/repos/ as a subdirectory is only writable by its owner. So mkdir -p /tmp/repos/claudelab.net may return no error, yet git clone into it will be rejected. That mismatch makes this tricky to debug.

The situation sometimes resets when a new session is started, but in scheduled task contexts, "manually intervene each time it breaks" is not a real solution. The code itself needs to detect and route around the problem.


The Fix: Automatic Fallback to $HOME/repos

The most reliable solution is automatically switching to $HOME/repos/ when /tmp/repos/ is not writable.

# Check /tmp/repos writability, fall back if needed
if [ -d /tmp/repos ] && [ ! -w /tmp/repos ]; then
  echo "⚠️ /tmp/repos not writable (likely nobody-owned) — switching to $HOME/repos"
  WORK="$HOME/repos/claudelab.net"
elif [ -w /tmp ] && mkdir -p /tmp/repos 2>/dev/null && [ -w /tmp/repos ]; then
  WORK="/tmp/repos/claudelab.net"
else
  WORK="$HOME/repos/claudelab.net"
fi
 
mkdir -p "$(dirname "$WORK")"
echo "📁 Working directory: $WORK"

$HOME in Cowork's VM resolves to /sessions/<session-name>/, which the current session always owns. It is also more persistent than /tmp/, making it well-suited for the persistent repository approach — keeping the cloned repository and updating it with git pull --rebase on subsequent runs, instead of re-cloning each time.


Combined Diagnostic: Permission Denied + ENOSPC

Both Permission denied and No space left on device (ENOSPC) will halt a git clone, but they require different responses. The following block checks both at the start of a scheduled task:

WS="$(ls -d /sessions/*/mnt/Dolice\ Labs 2>/dev/null | head -1)"
GITHUB_TOKEN=$(grep -A1 "Claude Lab" "${WS}/_documents/_github_tokens/github_tokens.txt" \
  | tail -1 | tr -d '[:space:]')
 
# --- 1. Check /tmp/repos writability ---
if [ -d /tmp/repos ] && [ ! -w /tmp/repos ]; then
  echo "⚠️ /tmp/repos owned by nobody — switching to $HOME/repos"
  WORK="$HOME/repos/claudelab.net"
else
  WORK="/tmp/repos/claudelab.net"
  mkdir -p /tmp/repos 2>/dev/null || WORK="$HOME/repos/claudelab.net"
fi
 
# --- 2. Check available disk space ---
FREE_MB=$(df "$(dirname "$WORK")" --output=avail -m 2>/dev/null | tail -1 | tr -d ' ')
echo "📊 Available: ${FREE_MB}MB (target: $WORK)"
 
if [ "${FREE_MB:-0}" -lt 300 ]; then
  echo "⚠️ Low disk — cleaning up old repositories"
  find "$(dirname "$WORK")" -maxdepth 1 -type d \
    -not -name "$(basename $WORK)" \
    -exec rm -rf {} + 2>/dev/null
  FREE_MB=$(df "$(dirname "$WORK")" --output=avail -m 2>/dev/null | tail -1 | tr -d ' ')
  echo "📊 After cleanup: ${FREE_MB}MB"
fi
 
if [ "${FREE_MB:-0}" -lt 200 ]; then
  echo "⛔ Still not enough disk space — aborting task"
  exit 1
fi
 
# --- 3. Clone or pull ---
if [ -d "$WORK/.git" ]; then
  cd "$WORK"
  git remote set-url origin "https://${GITHUB_TOKEN}@github.com/masakihirokawa/claudelab.net.git"
  git pull --rebase origin main || {
    rm -rf "$WORK"
    git clone --depth 1 "https://${GITHUB_TOKEN}@github.com/masakihirokawa/claudelab.net.git" "$WORK"
    cd "$WORK"
  }
else
  git clone --depth 1 "https://${GITHUB_TOKEN}@github.com/masakihirokawa/claudelab.net.git" "$WORK"
  cd "$WORK"
fi
 
git config user.email "masakihirokawa@gmail.com"
git config user.name "masakihirokawa"
echo "✅ Repository ready"

The instinct behind this defensive pattern comes from twelve years of individual app development. The crash reports that suddenly spiked for Beautiful HD Wallpapers before the v2.1.0 release were caught early because the monitoring was set up to expect failure — not just success. The same thinking applies here.


/tmp vs $HOME — When to Use Each in Cowork VMs

The right choice depends on how often the task runs and whether state needs to persist across executions.

When /tmp/repos/ works fine

For one-off scripts or tasks where a fresh clone every time is acceptable, /tmp is simpler to manage. The caveat is the nobody issue: always add the writability check before assuming it is available.

When $HOME/repos/ is the better choice

For scheduled tasks that run multiple times per day, $HOME/repos/ combined with the persistent repository strategy is significantly faster. Re-cloning a ~30 MB repository on every run adds unnecessary latency; pulling just the diff takes seconds instead.

For the four-site setup running Claude Lab, Gemini Lab, Antigravity Lab, and Rork Lab at four articles per day each, $HOME/repos/ with persistent repositories is the only approach that runs reliably. Combined, the four repositories stay at around 120 MB without node_modules — well within what the VM handles comfortably.


Updating Your SKILL.md

If you manage scheduled tasks through a SKILL.md file, add this four-line fallback detection at the top of Step 0:

# Add at the start of Step 0
_WORK_BASE="/tmp/repos"
if [ -d "$_WORK_BASE" ] && [ ! -w "$_WORK_BASE" ]; then
  _WORK_BASE="$HOME/repos"
fi
WORK="${_WORK_BASE}/claudelab.net"
mkdir -p "$_WORK_BASE" 2>/dev/null || WORK="$HOME/repos/claudelab.net"

One important note: when updating SKILL.md, also update the scheduled task prompt itself. The two are separate documents, and keeping them in sync prevents the task from reverting to old behavior on the next execution.


Quick Error Reference

When git clone fails, the error message usually tells you exactly what happened:

Permission denied — Directory ownership problem. Run ls -la on the parent directory to check who owns it.

No space left on device — Disk exhaustion. Run df -h and remove .next/ build caches or stale repositories.

Could not resolve host — Network access issue. Check whether the VM has internet access enabled.

Repository not found — Expired token or renamed repository. Regenerate the GitHub PAT and update SKILL.md.

index.lock: File exists — Previous git process crashed mid-operation. Remove .git/index.lock manually, or re-clone the repository.


What to Do Right Now

Run ls -la /tmp/repos/ and look at the owner column. If you see nobody nogroup, update your SKILL.md Step 0 with the four-line fallback pattern above. If you are hitting disk errors instead, add the ENOSPC diagnostic block before the clone step.

Both fixes take under five minutes to implement. The failure mode they prevent can silently break automated publishing for hours before anyone notices.

Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Cowork2026-06-30
Trusting a Three-Day-Old Mirror: Stopping Unattended Tasks from Acting on a Stale Working Copy
A persistent clone reused for speed quietly drifts from the remote. Read 'has this article been fixed yet?' from an out-of-sync tree and your unattended task duplicates work or 'succeeds' against an old world. Here is a HEAD-match plus writability preflight, with a self-healing re-clone, in bash.
Cowork2026-05-23
When pip install Stops with externally-managed-environment in Cowork's Bash Sandbox — Three Patterns for PEP 668
Why pip install fails with externally-managed-environment in Cowork's Bash sandbox, and three practical fixes — --break-system-packages, venv, and pipx — written from real scheduled-task experience.
Cowork2026-05-06
Claude in Chrome Not Working? A Practical Troubleshooting Guide
Buttons that do nothing, pages that never load, tasks that report success but change nothing. Symptom-by-symptom fixes, plus the 42-run log where adding waits and verification steps moved completion from 62% to 95%.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →