VS Code/code-server extension: resumed/new tabs silently ignore claudeCode.initialPermissionMode after extension update — permissionMode stuck undefined

Status Open
Reported on v2.1.225
Maintainer reply None cached
Activity 0 comments · opened Aug 8, 2026

Summary

After the extension auto-updates (observed: 2.1.225 → 2.1.226), previously-open and even brand-new chat tabs can silently lose "bypass permissions" mode and start prompting for approval on every single tool call — despite claudeCode.initialPermissionMode: "bypassPermissions" and claudeCode.allowDangerouslySkipPermissions: true being correctly set. There is no user-visible settings change; it looks like random breakage after an update, and there is no supported way to recover it project-wide.

Environment

  • code-server 4.115.0, extension anthropic.claude-code 2.1.226, linux x86_64
  • ~/.claude/settings.jsonpermissions.defaultMode: "bypassPermissions"
  • VS Code settings → claudeCode.initialPermissionMode: "bypassPermissions", claudeCode.allowDangerouslySkipPermissions: true

What the extension's own debug log shows

.../exthostN/Anthropic.claude-code/Claude VSCode.log, same extension host, same settings, different tabs:

Spawning Claude with SDK query function - ... permission mode: bypassPermissions, resume: <sessionA>
Spawning Claude with SDK query function - ... permission mode: undefined,          resume: <sessionB>
Spawning Claude with SDK query function - ... permission mode: undefined,          resume: undefined   <- brand-new conversation, first message ever

A genuinely brand-new conversation (resume: undefined) can already come up with permission mode: undefined.

Root cause (traced through the shipped, minified extension.js / webview/index.js)

  1. In the webview bundle, each conversation view-model initializes its permission-mode field to undefined (permissionMode=ct(void 0) in the minified build). It only gets populated from config.initialPermissionMode in specific construction paths (new-session creation, "teleport"). The path used when the webview reconnects and restores/resumes an already-open tab does not apply this fallback, so the field stays undefined for that tab's entire remaining lifetime.
  2. launchClaude(...) always includes permissionMode as an explicit key in the launch_claude message (even when undefined), which flows into the SDK query options (permissionMode:o inside spawnClaude). This appears to suppress the CLI's own --setting-sources-based resolution of permissions.defaultMode from ~/.claude/settings.json. Confirmed independently: invoking the packaged claude binary directly with no --permission-mode flag (same --setting-sources user,project,local) correctly resolves bypassPermissions and does not prompt — so the CLI-side resolution itself is fine; only the SDK-driven launch path from the extension is affected.
  3. Separately, migrateAllSettings() calls a one-time migration, clearPersistedPermissionModeOnce() (guarded by a permissionModeCleared20260804 globalState flag), which unconditionally clears any previously-persisted defaultPermissionMode from extension globalState the first time this code runs on a given machine/profile — with no re-derivation from current config afterward. On our machine it fired for the first time on today's auto-update to 2.1.226, immediately breaking every already-open tab that had implicitly been relying on that persisted value.

Impact

Any long-running or mostly-non-interactive workflow that keeps chat tabs open across days (common for background/automation-style usage of the extension) silently reverts to prompting on every tool call the moment the extension auto-updates — with no corresponding, visible settings change, so it presents as unexplained regression. We found no supported recovery short of manually re-toggling permission mode inside each affected tab's own UI one at a time.

Expected

Restoring/resuming an existing tab, or creating a brand-new conversation, should derive its initial permission mode from claudeCode.initialPermissionMode (subject to claudeCode.allowDangerouslySkipPermissions) whenever the user hasn't explicitly overridden it for that specific conversation — consistently, across all construction/restore paths, not just some of them.

Repro

  1. Set claudeCode.initialPermissionMode: "bypassPermissions" and claudeCode.allowDangerouslySkipPermissions: true.
  2. Open a chat tab, do some work.
  3. Reload the window / let the extension host restart (e.g. a code-server reconnect), or just leave the tab backgrounded and switch away and back.
  4. Tool calls in that tab now prompt for permission despite the settings above, and a brand-new tab created afterward is not guaranteed to be unaffected either.

View original on GitHub ↗