[BUG] claude.ai skills sync: sessions on different accounts (CLAUDE_SECURESTORAGE_CONFIG_DIR) share one synced bucket and churn it, filling ~/.claude/skills/.trash

Status Open
Reported on v2.1.285
Maintainer reply None cached
Activity 1 comment · opened Sep 30, 2026

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?

I run Claude Code sessions on more than one account at the same time on one Mac, each selected with CLAUDE_SECURESTORAGE_CONFIG_DIR (one credential store per account, one shared ~/.claude). Account skills sync treats them as one account on disk but not over the network, and when the accounts' skill lists differ, the two disagree on every sync.

The synced bucket directory, ~/.claude/skills/synced/<org>_<account>/, is named from the cached oauthAccount in the shared ~/.claude.json (in the 2.1.285 binary: organization from CLAUDE_CODE_ORGANIZATION_UUID or oauthAccount.organizationUuid, account from oauthAccount.accountUuid only). So every session writes the same bucket, whichever account it is signed in to. The skill list it reconciles against, though, comes from the account the session is actually signed in with (read in 2.1.283, and consistent with the cycle below). When the accounts have different skills enabled, a session removes from the shared bucket the skills its own account does not have, the other session puts them back, and every removed copy is moved into ~/.claude/skills/.trash/<epoch-ms>-<pid>-<suffix>/.

One cycle, watched with kqueue: a session on account A added 23 skills to the bucket, and 26 seconds later a session on account B moved all 23 into .trash. This repeats on the sync interval (10 minutes here, from the server flag tengu_claudeai_skills_sync_interval_minutes) for as long as sessions on both accounts are open.

The only cleanup of .trash I found is the age-based one that uses cleanupPeriodDays, the same setting that governs session transcript retention. So a long transcript retention also keeps every trashed skill copy for that long, and I found no setting that keeps transcripts long and .trash short. I have cleanupPeriodDays set to 36500 to keep my session history, so here built-in cleanup keeps these .trash entries for about 100 years.

There may also be leftover buckets after account changes: I have 10 bucket directories with modification dates from 2026-09-17 to 2026-09-29.

What Should Happen?

A session's synced skills should be keyed to the account that session is signed in with, so sessions on different accounts do not overwrite each other, and .trash should not grow while nothing on the user's side changes. Separately, trashed skill copies should have a retention of their own rather than sharing the transcript setting.

Error Messages/Logs

# ~/.claude/skills/.trash on one Mac, 2026-09-29 (local time)
18:14:32  9,394 entries
18:29:11  9,440 entries      +46 entries, +4.1 MB in 14 m 39 s  (about 17 MB/hour)
~19:10    9,509 entries, 886 MB

# 31 distinct skill names in .trash; the 23 that belong to account A appear about 358 times each.
# These are what remained after a daily local job moved 989 to 4,273 older entries per day
# out of .trash (2026-09-25 to 2026-09-29), so the rate of creation is higher than the count shows.

# ~/.claude/skills/synced/: 10 <org>_<account> bucket directories, modified 2026-09-17 to 2026-09-29

Steps to Reproduce

These steps reconstruct the situation I observed with long-running sessions; I have not re-run them from a clean start. Preconditions: two claude.ai accounts, A and B, where account A has at least one skill enabled on claude.ai that account B does not, and skills sync enabled for both. Choose a credential-store path you have never used, for example $HOME/.claude-issue-test-b. Note that step 1's /login replaces the login in your default credential store, and both steps rewrite the cached account identity (oauthAccount) in your shared ~/.claude.json.

  1. Start claude, run /login, choose account A, and leave the session open.
  2. In a second terminal, run CLAUDE_SECURESTORAGE_CONFIG_DIR="$HOME/.claude-issue-test-b" claude, run /login, choose account B, and leave this session open too.
  3. In a third terminal, note the starting count: ls ~/.claude/skills/.trash | wc -l (if the directory does not exist yet, the count is 0).
  4. Wait 20 to 30 minutes with both sessions open (they can be idle).
  5. Count again. Expected result: the count has grown, and the new entries are copies of account A's skills. The bucket being churned should be the one named from account B's identity, since step 2's /login set it last; ls ~/.claude/skills/synced/*/ at different moments shows account A's skills appearing and disappearing there.

Cleanup: in the account-B session run /logout, then exit both sessions and remove only the .claude-issue-test-b directory you created for this test. Then run /login to your usual account in a plain claude session, even if it is account A, so the cached oauthAccount matches your default login again. The copies added to .trash during the test stay there until cleanupPeriodDays removes them, and the test may leave an extra <org>_<account> directory for account B in ~/.claude/skills/synced/.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.285 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

Version notes: the bucket-naming code described above was read from the installed 2.1.285 binary and has the same shape in 2.1.283. The account-A session in the observed cycle had been open for days on 2.1.281; I did not record the other session's build.

Suggested fixes for the churn and the disk growth it causes:

  1. Key the bucket on the account the session actually authenticated with (the credential store in use), not the cached oauthAccount in the shared config file. This is the same class of problem as #87702 (a cache shared across CLAUDE_SECURESTORAGE_CONFIG_DIR profiles) and #79222 (the displayed identity taken from the shared cached oauthAccount).
  2. When reconciling, only trash items that this account itself synced into the bucket, rather than everything in the bucket that is not on this account's list.
  3. Give skills/.trash its own retention or size cap, independent of cleanupPeriodDays. This would not stop the churn, but it would bound the disk use.

Running several accounts on one machine is common: the claude.ai iOS app supports multiple accounts, and I do it in Claude Code with CLAUDE_SECURESTORAGE_CONFIG_DIR (its documentation is requested in #79223).

Workaround I am putting in place: launch the sessions on the non-primary accounts with --settings '{"syncClaudeAiSkills":false}', so only one account syncs and the sessions on those accounts get no claude.ai skills. I do not have a before and after for it yet. A local daily job also moves old .trash entries to the macOS Trash.

Related: #95373 (a different cause of skills landing in .trash, reported fixed in 2.1.280), #96816 (bucket marker file left behind when sync is disabled).

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗