Repository navigation
macOS: Child processes cannot access TCC-protected resources (Photos, Speech Recognition, Location, Contacts, etc.) #307364
Description
macOS: Child processes spawned from VS Code terminal/debug cannot access TCC-protected resources (Photos, Speech Recognition, Location, Contacts, etc.)
Problem
On macOS, processes spawned from VS Code's integrated terminal or debug adapter (e.g., flutter run -d macos, Electron apps, Swift CLI tools) cannot request or receive TCC permissions for most privacy-protected resources. The macOS permission dialog either never appears, or access is silently denied.
This affects all TCC-protected resources except Camera, Microphone, and Apple Events (which were added in #119787 / #95062).
Root Cause: macOS "Responsible Process" Model
macOS TCC (Transparency, Consent, and Control) uses a "responsible process" model. When VS Code spawns a child process via child_process.spawn(), node-pty, or the integrated terminal, macOS traces up the process tree and identifies VS Code as the responsible process for all TCC permission requests from that child.
When a child process requests access to a protected resource (e.g., Photo Library), macOS TCC checks:
- Does the responsible process (VS Code) have the corresponding
NS*UsageDescriptionkey in itsInfo.plist? - If yes → show permission dialog attributed to VS Code
- If no → silently deny the request (no dialog, no error — just empty/failure results)
VS Code's Info.plist currently includes usage descriptions for:
- ✅
NSMicrophoneUsageDescription - ✅
NSCameraUsageDescription - ✅
NSAppleEventsUsageDescription - ✅
NSAudioCaptureUsageDescription - ✅
NSBluetoothAlwaysUsageDescription(from Electron defaults)
But is missing descriptions for:
- ❌
NSPhotoLibraryUsageDescription— Photo Library - ❌
NSSpeechRecognitionUsageDescription— Speech Recognition - ❌
NSContactsUsageDescription— Contacts - ❌
NSCalendarsUsageDescription— Calendars - ❌
NSRemindersUsageDescription— Reminders - ❌
NSLocationUsageDescription— Location Services - ❌
NSLocalNetworkUsageDescription— Local Network discovery - ❌
NSAppleMusicUsageDescription— Media Library / Apple Music
Impact
Any developer building macOS apps that access these resources cannot develop or debug from VS Code. The app works when launched standalone or from Xcode, but fails when launched from VS Code's terminal or debug adapter. This is particularly confusing because:
- No error message explains why — TCC silently denies access
- The developer's own app has correct entitlements and
Info.plistkeys - The same code works when launched outside VS Code
- On macOS 15 (Tahoe) and later, the silent denial behavior is stricter
Common scenarios affected:
- Flutter desktop apps accessing Photo Library, Contacts, Location
- Electron apps using Speech Recognition, Media Library
- Swift/SwiftUI CLI tools or apps accessing any of these resources
- React Native macOS apps using native privacy-protected APIs
- Any
npm run/cargo run/ etc. that spawns a process needing TCC permissions
Reproduction
- Create a macOS app that requests Photo Library access (e.g., Flutter app using
photo_managerorimage_pickerpackage) - Ensure the app has correct
NSPhotoLibraryUsageDescriptionin its ownInfo.plistand correct entitlements - Launch the app from VS Code's integrated terminal via
flutter run -d macosor equivalent - Trigger the photo access feature
- Expected: macOS shows "Visual Studio Code wants to access the Photo Library" dialog
- Actual: No dialog appears, photo access silently fails or returns empty results
Compare: Launch the same app directly via open build/macos/Build/Products/Debug/MyApp.app — the permission dialog appears correctly.
Proposed Fix
Add the missing NS*UsageDescription keys to VS Code's Info.plist during the build signing step in build/darwin/sign.ts, following the exact same pattern already used for Camera, Microphone, and Apple Events:
// These already exist:
await spawn('plutil', ['-insert', 'NSAppleEventsUsageDescription', '-string',
'An application in Visual Studio Code wants to use AppleScript.', infoPlistPath]);
await spawn('plutil', ['-replace', 'NSMicrophoneUsageDescription', '-string',
'An application in Visual Studio Code wants to use the Microphone.', infoPlistPath]);
// Proposed additions (same pattern):
await spawn('plutil', ['-insert', 'NSPhotoLibraryUsageDescription', '-string',
'An application in Visual Studio Code wants to access the Photo Library.', infoPlistPath]);
await spawn('plutil', ['-insert', 'NSSpeechRecognitionUsageDescription', '-string',
'An application in Visual Studio Code wants to use Speech Recognition.', infoPlistPath]);
await spawn('plutil', ['-insert', 'NSContactsUsageDescription', '-string',
'An application in Visual Studio Code wants to access Contacts.', infoPlistPath]);
await spawn('plutil', ['-insert', 'NSCalendarsUsageDescription', '-string',
'An application in Visual Studio Code wants to access Calendars.', infoPlistPath]);
await spawn('plutil', ['-insert', 'NSRemindersUsageDescription', '-string',
'An application in Visual Studio Code wants to access Reminders.', infoPlistPath]);
await spawn('plutil', ['-insert', 'NSLocationUsageDescription', '-string',
'An application in Visual Studio Code wants to access your Location.', infoPlistPath]);
await spawn('plutil', ['-insert', 'NSLocalNetworkUsageDescription', '-string',
'An application in Visual Studio Code wants to access the Local Network.', infoPlistPath]);
await spawn('plutil', ['-insert', 'NSAppleMusicUsageDescription', '-string',
'An application in Visual Studio Code wants to access the Media Library.', infoPlistPath]);No entitlement changes in app-entitlements.plist are needed — VS Code is not sandboxed (com.apple.security.app-sandbox is absent), so the Info.plist usage descriptions alone are sufficient for TCC to show permission dialogs.
Security Consideration
This was discussed in #119787. Adding usage descriptions does not grant VS Code automatic access — it only allows macOS to show the permission dialog when a child process requests access. The user still has to explicitly approve each permission, and can revoke it at any time in System Settings → Privacy & Security.
The same trade-off accepted for Camera, Microphone, and Apple Events applies here: any extension or terminal process could trigger the dialog, but the user always has the final say. This is the same model used by Terminal.app, iTerm2, and Xcode.
Long-Term: responsibility_spawnattrs_setdisclaim()
A more granular long-term solution would be to use the responsibility_spawnattrs_setdisclaim() mechanism (as adopted by Qt Creator) to make child processes their own responsible process for TCC purposes. This would let child apps show permission dialogs attributed to themselves rather than to VS Code. However, this requires native code changes across node-pty, potentially Electron's utilityProcess, and the debug adapter spawn path — a much larger effort.
Environment
- VS Code: 1.114.0
- macOS: 26.4 (25E246) — but the issue exists on all macOS versions with TCC
- Affected file:
build/darwin/sign.ts(lines 98–127)
Related Issues
- macOS entitlements and entries for Info.plist disallow developing and running certain types of apps via VSCode #119787 — Original fix for Apple Events, Camera, Microphone (closed 2021)
- VSCode terminal doesn't allow/request permissions to access media devices #95062 — VSCode terminal doesn't allow/request permissions to access media devices (closed 2021)
Activity
deepak1556 commentedon Apr 2, 2026
I agree with the issue but we cannot keep adding all the usage descriptions to VSCode application. We can start with disclaiming tcc for the pty host process which is already possible.
CVirus commentedon May 12, 2026
Confirmed on macOS 26.4.1 — tccd actively suppresses the prompt with Policy disallows prompt for Sub:{com.microsoft.VSCode}.... The same
icalbinary works fine from Terminal/iTerm/Zed/kitty on the same machine, isolating it to VSCode's missing entitlements.feat(18-02): implement parent-terminal.ts and marker.ts (GREEN)
111fanyj commentedon Jun 16, 2026
Or is there a way for us to show some kind of permission-related error or prompt? After testing for a while, we found that both the system terminal and third-party terminals work fine. We checked the app’s Info.plist and confirmed the root cause. This is really annoying.
pgolawsk commentedon Sep 6, 2026
+1 — running into this with EventKit-based CLI tools, not just debugged apps.
Use case: shelling out to tools like
keith/reminders-cli(and Calendar/Contacts equivalents) from the integrated terminal — increasingly via coding agents that run commands for you (Claude Code, Codex, etc.). From Terminal.app or iTerm2 the tool triggers a TCC prompt once and then works. From the VS Code integrated terminal it fails instantly witherror: you need to grant reminders accessand no prompt ever appears, becausecom.microsoft.VSCodeships noNSRemindersUsageDescription.The user cannot work around it:
+"add application" button for the Reminders / Calendar / Contacts panes, so VS Code can't be added by hand.tccutil reset Reminders com.microsoft.VSCodedoesn't help — there's no usage string, so nothing to prompt for.Same root cause is being reported all over downstream: keith/reminders-cli#68 & #58, openai/codex#21228, anthropics/claude-code#63032.
Implementation note: on macOS 14+ EventKit split into granular scopes, so the current key names are
NSRemindersFullAccessUsageDescriptionandNSCalendarsFullAccessUsageDescription(the legacyNSRemindersUsageDescription/NSCalendarsUsageDescriptionstill work as fallback). Adding these inbuild/darwin/sign.tsalongside the existing Camera/Mic/AppleEvents entries (per #119787) would resolve the whole class.Complementary approach worth considering: call
responsibility_spawnattrs_setdisclaim()when spawning integrated-terminal / debug child processes, so each child becomes its own responsible process and shows a prompt attributed to itself rather than to VS Code. Qt Creator went this route (writeup); standalone implementation intorarnv/disclaim. It only helps child binaries that carry their ownInfo.plist, so it's additive to the usage-string fix, not a replacement — but it also stops VS Code's name from showing on prompts for things a child tool requested.M26: the documentation for the platform, CI and voice changes
M26: the documentation for the platform, CI and voice changes
M28: macOS dictation asks under its own name; the release token's env…