[BUG] VS Code extension: session history ignores the config directory when `claudeCode.claudeProcessWrapper` is set

Status Open
Reported on v2.1.284
Maintainer reply None cached
Activity 3 comments · opened Sep 29, 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?

VS Code extension: session history ignores the config directory when claudeCode.claudeProcessWrapper is set

Component: Claude Code VS Code extension
Version: 2.1.284 (darwin-arm64), VS Code Insiders 1.140.0-insider, macOS 26.7
Severity: Past sessions vanish from the sidebar with no error, for every user of a process-wrapper setup. No data is lost.

Summary

When claudeCode.claudeProcessWrapper points to a wrapper that sets CLAUDE_CONFIG_DIR, the extension's session history list still reads ~/.claude/projects. It does not read the directory the CLI actually uses. The config directory reported by claude auth status --json is thrown away when a wrapper is set. An absolute CLAUDE_CONFIG_DIR in claudeCode.environmentVariables is also not used for the list. The only thing that works is having CLAUDE_CONFIG_DIR in VS Code's own launch environment, and a normal launch from the Dock or Finder never has it.

Why we use a wrapper

We run Claude Code in VS Code Insiders against AWS Bedrock with its own config directory (~/.claude-api), next to a personal subscription in regular VS Code (~/.claude). The wrapper sets CLAUDE_CONFIG_DIR and the Bedrock environment, then execs the bundled binary. We use a wrapper rather than a launch-time environment for two reasons: the launch environment is lost whenever Insiders restarts itself for an update, and the wrapper can refuse to start without Bedrock credentials.

What Should Happen?

Suggested fix

Any one of these would solve it for us:

  1. When claudeCode.claudeProcessWrapper is set, honor the configDirectory (or projectsDirectory) reported by claude auth status --json for the history list. If the reason for ignoring it is a trust concern, honoring it for listing alone would be enough.
  2. Or: when an absolute CLAUDE_CONFIG_DIR is in claudeCode.environmentVariables, use it in l0() before process.env and the ~/.claude fallback. vr() already computes this value for the CLI's environment.
  3. At minimum, document that session history under a process wrapper follows VS Code's launch environment rather than the wrapper or environmentVariables.

Workaround we are giving users

Past sessions can still be resumed from the terminal inside VS Code Insiders with claude --resume, which lists from the CLI's own config directory. Launching VS Code with CLAUDE_CONFIG_DIR in its environment isn't a workable instruction for our users. It is lost whenever VS Code restarts itself for an update. Setting it machine-wide with launchctl setenv would also redirect the separate VS Code install that uses the personal subscription.

Error Messages/Logs

Steps to Reproduce

Steps to reproduce

  1. Create a wrapper, for example ~/bin/claude-alt:

``bash
#!/usr/bin/env bash
export CLAUDE_CONFIG_DIR="$HOME/.claude-alt"
exec "$@"
``

  1. In VS Code user settings:

``json
"claudeCode.claudeProcessWrapper": "/Users/<you>/bin/claude-alt"
``

  1. Launch VS Code from the Dock, so CLAUDE_CONFIG_DIR is not in its environment.
  2. Open a folder and have a short conversation in the Claude panel. The transcript is written to ~/.claude-alt/projects/<folder>/<id>.jsonl, which is correct.
  3. Run Developer: Reload Window and open the session history.

Expected: the conversation from step 4 is listed.
Actual: it is missing. The list shows whatever is in ~/.claude/projects/<folder>/, which is usually nothing, or sessions from a different account.

  1. Also add the directory to the settings and reload again:

``json
"claudeCode.environmentVariables": [{ "name": "CLAUDE_CONFIG_DIR", "value": "/Users/<you>/.claude-alt" }]
`
**Actual:** still missing. We confirmed this over two window reloads after the setting was saved. The extension host process restarted each time, and its environment had no
CLAUDE_CONFIG_DIR`.

Based on the code below, the list should appear if VS Code is started with open -a "Visual Studio Code - Insiders" --env CLAUDE_CONFIG_DIR=$HOME/.claude-alt. We have not tested that yet.

Root cause (from the 2.1.284 bundle; minified names)

The history directory resolves as:

function l0(){ return Mg ?? eX() }
function eX(){ if(process.env.CLAUDE_CONFIG_DIR) return process.env.CLAUDE_CONFIG_DIR;
               return H4.join(hG.homedir(), ".claude") }
function iW1(){ return H4.join(l0(), "projects") }

Mg is set only by rW1(...), and its only caller is refreshCliAuthStatus(). That function runs claude auth status --json through the wrapper, which correctly reports configDirectory. It then discards the value when a wrapper is in use:

let $ = this.getClaudeBinary(), J = $.viaProcessWrapper === !1, ...
...
if (!J) Z = { value: void 0 };
else if (typeof F === "object" && F !== null && "configDirectory" in F) Z = { value: F.configDirectory };
...
if (Z !== void 0) rW1(Z.value, ...)

With a wrapper, Mg is always undefined. l0() then falls back to the extension host's own process.env.CLAUDE_CONFIG_DIR, and after that to ~/.claude. claudeCode.environmentVariables is merged only into the spawned CLI's environment (q8()), never into l0(). listSdkSessions() then calls the default SDK listing (z2() returns undefined because l0() === eX()), which scans ~/.claude/projects.

The CLI's own projectsDirectory from the same probe is kept (cachedCliProjectsDirectory) and used for the resume precheck. So the extension resumes from the right directory but lists from the wrong one.

Claude Model

Opus

Is this a regression?

No, this never worked

Last Working Version

_No response_

Claude Code Version

2.1.283

Platform

AWS Bedrock

Operating System

macOS

Terminal/Shell

VS Code integrated terminal

Additional Information

_No response_

View original on GitHub ↗

3 Comments

BasedGPT · 14 days ago

The extension is querying ~/.claude/projects while the wrapper writes transcripts under ~/.claude-api/projects. The files remain intact, but the VS Code Insiders list is reading from a different store.

I built the VS Code checks in claude-code-session-recovery (BasedGPT/claude-code-session-recovery). First check the global hidden-session list and the Insiders stores with this read-only audit:

~~~sh
python tools/sessions/audit_vscode_session_surfaces.py \
--projects-dir "$HOME/.claude-api/projects" \
--workspace-dir "$HOME/Library/Application Support/Code - Insiders/User/workspaceStorage" \
--global-state-db "$HOME/Library/Application Support/Code - Insiders/User/globalStorage/state.vscdb"
~~~

The audit prints counts and whether the global hidden-session list overlaps local transcripts; it does not print session IDs or write to either store. If it completes with a hidden-session overlap, stop there because a workspace cache injection cannot override that filter.

If the audit completes without a hidden-session overlap, preview the cache scan:

~~~sh
python tools/sessions/recover_vscode_sessions.py \
--projects-dir "$HOME/.claude-api/projects" \
--workspace-dir "$HOME/Library/Application Support/Code - Insiders/User/workspaceStorage"
~~~

This is a dry run. If it lists the affected sessions as missing from the cache, close VS Code Insiders fully and rerun that command with --apply. It backs up the cache and leaves the JSONL transcripts unchanged. If those sessions are already in the cache, the extension's directory choice is the unresolved part and a cache write will not help.

The tools are in BasedGPT/claude-code-session-recovery, where the audit and cache scan both support explicit Insiders and alternate-config paths.

Hope this helps, if my tools are able to help you, would appreciate a ⭐ :)

martaver · 9 days ago

Still present in 2.1.289 (darwin-arm64). The discard is unchanged in refreshCliAuthStatus():

J=$.viaProcessWrapper===!1, ...
!J)N={value:void 0};else if(typeof L==="object"&&L!==null&&"configDirectory"in L)N={value:L.configDirectory}

There is a second consequence that we think is worse than the missing history: restored tabs start new sessions. When a window reloads, or VS Code restarts, a panel tab whose session is not in that list does not resume. The webview sends launch_claude without resume, and the log shows Spawning Claude with SDK query function - cwd: …, /path/to/wrapper, resume: undefined. Every open conversation is replaced by an empty one on each restart. The transcripts are intact on disk, and claude --resume <id> in a terminal still finds them.

What we saw, with a wrapper that picks CLAUDE_CONFIG_DIR from the folder a session starts in:

  • In our extension logs since we set up the wrapper, every restored tab that resumed was a session that still had an older copy of its transcript in ~/.claude/projects/<slug>/, left over from before the wrapper. No session created after the switch was ever resumed on restore.
  • A window gets the environment of the code command that opened it. So when a folder is opened from a terminal where another folder's environment is loaded, the extension host has that folder's CLAUDE_CONFIG_DIR. The list and the restore check then read a third config dir, neither ~/.claude nor the one the wrapper uses.
  • await_config_home_request returns at once and the tab still starts fresh, because nothing ever follows the config home under a wrapper.

Requested fix: when claudeCode.claudeProcessWrapper is set, use the configDirectory (or projectsDirectory) reported by claude auth status --json for the session list. That is suggestion 1 above. Restored tabs should also wait for that probe before deciding a session cannot be resumed.

Workaround, for anyone else hitting this:

  1. Have the wrapper keep ~/.claude/projects/<slug> as a symlink to $CLAUDE_CONFIG_DIR/projects/<slug> for the directory it starts in. <slug> is the absolute real path with every non-alphanumeric character replaced by -. Windows started from the Dock then list and resume correctly.
  2. Open VS Code windows without CLAUDE_CONFIG_DIR in the environment, for example with direnv exec / code ….
wimdecorte · 9 days ago

Some context on why we run two config dirs at all: we run two Claude Code setups side by side on the same Mac. Regular VS Code uses a personal Claude subscription with the default ~/.claude. VS Code Insiders routes Claude Code through Amazon Bedrock via claudeCode.claudeProcessWrapper, and the wrapper sets CLAUDE_CONFIG_DIR=~/.claude-api so the two logins, settings and transcripts never mix. That's also why the ~/.claude/projects/<slug> symlink above isn't an option for us: it would merge the two stores, and the subscription side would list (and could resume) Bedrock conversations.

Confirmed on 2.1.289 that restore runs through the same list: fF5 resumes a saved tab only if its id is in the session list, otherwise it opens fresh_after_missing_saved. Our Insiders logs from before our workaround show zero restored resumes.

One detail for anyone reading their logs: a panel tab is offered for restore only if its session was updated in the last 10 minutes (AL0 = 600000). After an overnight restart, a blank tab is expected even when the list is correct.

Our workaround, for setups with one alternate config dir per app (not per folder). On macOS, VS Code launched from the Dock or Spotlight copies the login shell's environment into the extension host, and every process an app launches carries __CFBundleIdentifier. A window opened with the code CLI skips the login shell (VSCODE_CLI=1) and takes the calling terminal's environment instead, hence the function. In ~/.zshrc:

if [[ "${__CFBundleIdentifier:-}" == "com.microsoft.VSCodeInsiders" ]]; then
  export CLAUDE_CONFIG_DIR="$HOME/.claude-api"
fi
function code-insiders { CLAUDE_CONFIG_DIR="$HOME/.claude-api" command code-insiders "$@"; }

This needs a full quit and relaunch; Reload Window isn't enough. After that, both the history list and tab restore work, and the regular VS Code side is never touched. It can't do per-folder dirs like martaver's wrapper. The real fix is still suggestion 1: when a wrapper is set, use configDirectory from claude auth status --json.