[BUG] Task tools always fail with EPERM (utimensat on lockfile) when CLAUDE_CONFIG_DIR is on Cloud Storage FUSE (Cloud Run + Agent SDK)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
When Claude Code runs headlessly via the Agent SDK with CLAUDE_CONFIG_DIR located on a Cloud Storage FUSE (gcsfuse) volume — the standard way to persist session state for an SDK service on Google Cloud Run — every TaskCreate / TaskUpdate call fails with:
EPERM: operation not permitted, utimensat '<CLAUDE_CONFIG_DIR>/tasks/<session-id>/.lock.lock'
Root cause (verified against the v2.1.162 binary): the task store under $CLAUDE_CONFIG_DIR/tasks/<session>/ serializes writes with a lockfile (bundled proper-lockfile). proper-lockfile updates the lock's mtime via utimens/utimensat for staleness detection. gcsfuse does not support setting arbitrary mtimes through setattr (a long-standing, documented limitation — GoogleCloudPlatform/gcsfuse#33), so lock acquisition always throws EPERM and the whole tool call fails.
Consequences in this environment:
- Since v2.1.142 the Task tools replaced
TodoWriteas the default session checklist (tools reference), so task tracking is now entirely broken for SDK deployments that persistCLAUDE_CONFIG_DIRon gcsfuse. - The periodic
task_reminderkeeps nudging the model to callTaskCreate, the call fails withEPERM, and the loop repeats — wasted calls and confusing error chips for end users. - There is no escape hatch: the tasks directory is hard-derived from
CLAUDE_CONFIG_DIR(no separate override), and the lock's mtime touch cannot be disabled.
What Should Happen?
Task tools should work — or degrade gracefully — on filesystems that don't support utimensat. Any of these would solve it:
- Graceful degradation: treat the mtime-touch failure (
EPERM/ENOSYS) as non-fatal and fall back to lock acquisition without staleness touching, rather than failing the entire tool call. - Configurable task store location (e.g.,
CLAUDE_CODE_TASKS_DIR): allow pointing the task store at a local writable filesystem while sessions remain on the FUSE mount. - Automatic fallback: if the task store is unusable, disable the Task tools for the session and re-enable the in-memory
TodoWriteinstead of exposing tools that can never succeed.
Error Messages/Logs
# tool_result (is_error: true) returned for every TaskCreate call, e.g.:
EPERM: operation not permitted, utimensat '/app/.claude-admin-help-assistant/user_xxx/tasks/04fc8dea-5320-4ec5-a451-356472416497/.lock.lock'
Session transcripts show consecutive TaskCreate attempts all failing identically.
Steps to Reproduce
- Deploy a service to Cloud Run (gen2) with a Cloud Storage volume mount:
````
--add-volume=name=claude-config,type=cloud-storage,bucket=<bucket>
--add-volume-mount=volume=claude-config,mount-path=/app/.claude-config
- Call the Agent SDK with the config dir on that mount (claude-code CLI ≥ 2.1.142, so Task tools are enabled by default):
```ts
import { query } from '@anthropic-ai/claude-agent-sdk'
query({
options: {
env: { CLAUDE_CONFIG_DIR: '/app/.claude-config/some-user' },
// ...
},
})
```
- Send any prompt that makes the model track multi-step progress (or just wait for the
task_reminderto nudge it into callingTaskCreate). - Every
TaskCreate/TaskUpdatereturns theEPERM: operation not permitted, utimensat ...error above.
Minimal repro of the underlying FS limitation without Cloud Run: mount any bucket with gcsfuse and touch an existing file inside the mount → touch: setting times of '...': Operation not permitted.
Current workaround: set CLAUDE_CODE_ENABLE_TASKS=0 to re-enable the in-memory TodoWrite (which never touches the filesystem, so it is unaffected) — at the cost of relying on a deprecated tool.
Claude Model
Sonnet (default)
Is this a regression?
Yes, this worked in a previous version
Last Working Version
2.1.141 (before v2.1.142, the default session checklist was the in-memory TodoWrite, which is unaffected by the gcsfuse limitation; the Task tools themselves have never worked on gcsfuse)
Claude Code Version
2.1.162 (Claude Code)
Platform
Anthropic API
Operating System
Other Linux
Terminal/Shell
Non-interactive/CI environment
Additional Information
- Environment: Google Cloud Run (gen2) + Cloud Storage FUSE volume mount; Claude Code spawned headlessly by
@anthropic-ai/claude-agent-sdk(query()), one process per request withresume. - gcsfuse limitation reference: https://github.com/GoogleCloudPlatform/gcsfuse/issues/33 (arbitrary mtime via
setattrunsupported; no mount option exists to allow it). - This deployment shape (serverless + FUSE-mounted bucket for
CLAUDE_CONFIG_DIRpersistence across instances) is common for Agent SDK services, so any such deployment on a current CLI hits this. - Related but distinct lock-mechanism reports: #22166 (stale
history.jsonl.lockfreeze), #22169 (tasks/.locknot released). Neither covers the unsupported-utimensatfailure mode.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗