Desktop app (macOS) ignores PATH from all sources — Read tool cannot find poppler/pdftoppm
Environment
- Claude Desktop app version: 1.2.234
- OS: macOS 15.7.2 (Sequoia), Apple Silicon (arm64)
- Shell: bash
- Homebrew prefix: /opt/homebrew
- poppler version: 26.03.0 (installed via
brew install poppler)
Description
The Claude Code Desktop app's Read tool cannot find pdftoppm (poppler) for PDF rendering. The Bash tool also operates with a minimal 4-entry PATH (/usr/bin:/bin:/usr/sbin:/sbin) that excludes Homebrew, MacPorts, and /usr/local/bin paths.
The VS Code Claude Code extension does pick up the correct PATH from settings.json env. This is a Desktop-app-specific issue.
Steps to Reproduce
- Install poppler:
brew install poppler - Verify it works:
/opt/homebrew/bin/pdftoppm -vreturnspdftoppm version 26.03.0 - Open Claude Desktop app
- Ask Claude to read any PDF file using the Read tool
- Result:
pdftoppm is not installed. Install poppler-utils... - Ask Claude to run
echo "$PATH" | tr ':' '\n'via Bash tool - Result: Only
/usr/bin,/bin,/usr/sbin,/sbin— no Homebrew, no MacPorts, no/usr/local/bin
Expected Behavior
The Read tool should find pdftoppm when it is installed and available via standard PATH locations (/opt/homebrew/bin, /usr/local/bin), or when PATH is configured via settings.json env, or when PATH is set via launchctl setenv.
Workarounds Attempted — ALL FAILED for Desktop App
1. Homebrew install (default location)
brew install popplerinstalls to/opt/homebrew/bin/pdftoppm- Desktop app PATH does not include
/opt/homebrew/bin - Result: Not found
2. Symlink to /usr/local/bin
sudo ln -s /opt/homebrew/bin/pdftoppm /usr/local/bin/pdftoppm
sudo ln -s /opt/homebrew/bin/pdftotext /usr/local/bin/pdftotext
sudo ln -s /opt/homebrew/bin/pdfinfo /usr/local/bin/pdfinfo
- Symlinks confirmed working via full path
- Desktop app PATH does not include
/usr/local/bin - Result: Not found
3. settings.json env PATH
{
"env": {
"PATH": "/opt/local/bin:/opt/local/sbin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
}
}
- Full quit and restart of Desktop app after edit
- Bash tool still shows minimal 4-entry PATH
- Note: This same setting DOES work for the VS Code extension — VS Code Claude Code sees the full configured PATH
- Result: Not found (Desktop), Works (VS Code)
4. launchctl setenv PATH
launchctl setenv PATH /usr/local/bin:/usr/local/sbin:/opt/local/bin:/opt/local/sbin:/opt/homebrew/bin:/opt/homebrew/sbin:/bin:/usr/bin:/usr/sbin
- Confirmed via
launchctl getenv PATH— value is set correctly - Full quit and restart of Desktop app after setting
- Interesting: OTHER environment variables set via
launchctl setenvARE visible to the Desktop app (confirmed by checking custom env vars via Bashsetcommand). Only PATH appears to be overridden. - Result: Not found — PATH specifically is being overridden by the Desktop app
5. Shell snapshots
~/.claude/shell-snapshots/*.shcontains the full PATH with/opt/homebrew/binand/usr/local/bin- Desktop app does not apply this PATH to the tool environment
- Result: Not found
6. Session restarts
- Multiple full quit-and-restart cycles after each workaround
- Ensured no lingering Claude or Chrome hook processes
- Result: Not found after any restart
Key Finding
The Desktop app picks up other environment variables from launchctl setenv (confirmed by checking non-PATH env vars), but PATH specifically is being overridden with a hardcoded minimal value. This suggests the Electron app or its utility process is explicitly constructing/replacing PATH rather than inheriting it from the session environment.
Per issue #22559, the Desktop app extracts PATH via a "utility process" — this process appears to be producing a minimal system PATH and discarding any user-configured PATH values from all sources (launchctl, settings.json, shell profiles, shell snapshots).
Workaround That Does Work
Calling pdftoppm via the Bash tool using the full absolute path (/opt/homebrew/bin/pdftoppm) works. The binary exists and functions correctly — it is only the PATH-based discovery that fails.
Related Issues
- #22559 — Desktop app does not forward env vars to CLI subprocess (documents the "utility process" PATH extraction)
- #22512 — settings.json env vars not applied to CLI subprocess (OPEN)
- #23704 — Read tool PDF support requires undocumented poppler-utils
- #29886 — Read tool can't find poppler in headless mode
- #7490 — Shell configuration not inherited by Bash tool (91 upvotes)
- #31799 — Mentions settings.json env PATH as fix (works for CLI/VS Code, not Desktop)
25 Comments
Found 1 possible duplicate issue:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
The same PATH stripping by the
disclaimerbinary also breaks the Claude Preview tool when using Next.js with Turbopack.What happens: The preview server spawns
nodevia the absolute path inlaunch.json, which works. But Turbopack (Rust-based) internally spawnsnodeas a child process for PostCSS/CSS processing via its "node pooled process" worker pool. Since it resolvesnodefrom PATH, it fails:Closed https://github.com/anthropics/claude-code/issues/42639 as a duplicate of this. Adding context:
This is a regression. The v2.1.78 release notes explicitly state: "Fixed Bash tool not finding Homebrew and other PATH-dependent binaries when VS Code is launched from Dock/Spotlight." This fix regressed by v2.1.87.
The shell snapshot correctly captures functions and aliases from profile scripts (proving they ran) — but the final export PATH= line reflects the parent process's minimal PATH, not the login shell's resolved PATH.
I have this issue too.
Someone please fix. I can't use Claude Desktop at the moment and I really want to experiment with the preview tooling!
@druvv the workaround that @thatChadM mentioned of "calling pdftoppm via the Bash tool using the full absolute path (/opt/homebrew/bin/pdftoppm)" has been working for me thus far. it's not a actual fix by anthropic but if you put this as a rule in memory.md it works again. its weird though because code on desktop used to handle PDF operations seamlessly..
This breaks everything, not just pdftoppm. many tools not accessible.
The call via the bash tool using full path method should work for lots of stuff inside Desktop directly, but agents are still mostly out of luck. They get the memory telling them how to use the tools via bash but have no permission to execute it (outstanding agent permission inheritance bug) and if the agent is backgrounded the permission request never bubbles up to the user. Agents have to be foregrounded (blocking) for the permission request to make it to the user to approve.
if claude code was open-source, I could fix this issue on my local already and stop wasting my time approving commands that I already approved in my whitelisted permissions.
This appears to have been fixed in Desktop app version 1.1062.0 (b81bcd). Getting a complete path in both main Desktop, foreground agent, and background agent and all 3 can properly use the Read tool to view pdfs again. No need for the env PATH hack or launchctl setenv hack. Confirmed that VSC plugin v 2.1.92 still works properly as well.
Saving this chunk of my CLAUDE.md for reference in case this bug resurfaces & the workaround is necessary again:
The desktop fix does now include more paths in
$PATHand some command are now working, but it still uses its own custom$PATHwith a stripped down list of paths:For mean, this means that Claude Code on desktop still misses
uv(~/.local/bin/uv) andbun(~/.bun/bin/bun). Where as Claude Code CLI starts with my full configured$PATH(which includes~/.local/binand~/.bun/bin).CLI, and apparently VSC plugin (somehow), are picking up and using the shell's PATH. Not sure how VSC plugin is doing it since VSC is launched via GUI rather than shell and thus should get the launchctl PATH (which isn't set by default, so should just be the default directories). I'm guessing VSC plugin is using a bundled version of the CLI that is somehow being started via shell & thus picks up the shell's PATH.
Desktop is setting up the path internally on its own it appears and this update just added common locations into the path. Confirmed that the env PATH in settings.json is ignored by desktop and PATH set via launchctl is also ignored. Since the app is typically launched via gui and not the shell, that means that environment variables (like PATH) that are set in shell profiles are never, and will never be, picked up automatically on typical GUI app start so building a default PATH list makes sense.
Not sure if there is another approach that can be used to source the shell path from system & user profiles. Maybe internally spawn the user's shell at launch, grab PATH, and use that in addition to whatever the built-in list is? Or actually honor setting the PATH via env in settings.json? Not sure what would be preferred and/or safer.
Net of all that is that built-in tools -should- be working again if they are installed in any of the well-known locations that the app currently looks in, but anything outside of the builtin PATH won't be found without the bash execution workaround.
This may be the case, but it's also true that previously (at least for myself)
uvandbunwhere available to Claude Code Desktop without any extra settings/config monkey-ing around to update$PATHwithuvandbun's paths.(This feels especially odd given that Anthropic owns Bun, so they must have run into this problem themselves. I suspect that Claude Code Desktop is lower priority for Anthropic than Claude Code CLI. If true, that would be a shame since a GUI I think affords much more powerful tooling/interactions with Claude Code than a terminal can.)
I dunno how it used to work or what changed in the Desktop app since I only recently started using it. I just know that I've ran into similar environment inheritance issue with other GUI apps and had to poke the environment variables in via launchctl so that GUI-launched apps would get them. Drove me nuts for quite a while trying to figure out why I could see the variable in the shell but the app itself couldn't see it before I finally realized that Mac doesn't launch the shell & then launch the desktop interface via that shell, it gets kicked off directly from launchd so GUI apps only know the environment that is inherited from launchd.
Since I was primarily confirming the broken tools were working, I completely overlooked that some other custom directories in my path, like ~/Dropbox/bin, were missing from Desktop's environment until you posted your findings (thank you!) & I went and checked closer. I'm curious what approach was used originally that was picking up shell's path but have no way of knowing. Hopefully there is some plan already in process for finding/setting/inheriting more customized PATHs to accommodate different uses cases & users.
Troubleshooting super sluggishness & strange behavior in Desktop Code w/VSC Code, along with some permission oddness in VSC, and VSC found this interesting little tidbit in the logs:
So apparently the fix was to look for something well-known in the path and if not found add those common directories to the path.
This issue appears to be solved fully!
<img width="640" height="376" alt="Image" src="https://github.com/user-attachments/assets/0ed32ac5-ecd0-4f55-bd2a-daf4b1ce73b9" />
I agree, it looks like Claude 1.1348.0 (efa3fd) 2026-04-08T15:06:08.000Z fixed it. I am seeing the full path from shell profile in the main Code instance as well as both foreground & background agents. Interestingly, whatever it was that was changed means there is no longer any sort of PATH warning getting logged at startup like I saw on the prior version. Really kinda curious exactly what they are doing under the covers; VSC's guess is that they are having the Electron layer launch the shell during startup just to grab the shell profile's PATH and then sticking it into the process environment so that the main app & agents get it. If that's the case then I wonder what changed that broke that behavior. shrug Oh well, no biggy, that's just idle curiosity on my part.
Since this had some visibility & activity from others I'll leave it open for now in case anyone else sees a problem. Either it will auto-close or I'll close it in a few days if no one else chimes in with a problem.
Root Cause Analysis
I've traced this to two specific mechanisms in the Desktop app:
1. PATH extraction uses
-i(interactive) flagThe Electron app extracts PATH at launch by spawning:
The
-iflag loads.zshrc, which triggers terminal-dependent plugins (prompt themes, syntax highlighting, autosuggestions, plugin managers). The Electron utility process has no TTY, so these fail and the extraction exits with code 1.The fix: use
zsh -l -cinstead ofzsh -l -i -c. A non-interactive login shell loads.zprofile(which builds PATH) but skips.zshrc(which has the terminal-dependent plugins).2. Shell snapshot hardcodes the broken PATH
At session start, Claude writes a snapshot file (
~/.claude/shell-snapshots/snapshot-zsh-*.sh) containing:This is the fallback PATH from step 1. Every Bash tool call sources this snapshot after shell init, so it overwrites whatever PATH the shell built during init. This is why no user-side fix works —
.zshenv,.zprofile,launchctl setenv,settings.jsonenv — the snapshot clobbers them all.Why
/opt/homebrew/binsurvives but nothing elseHomebrew installs
/etc/paths.d/Homebrew, so/opt/homebrew/binis part of the system default PATH from/etc/paths. Everything from.zprofile(~/.local/bin,~/.cargo/bin, conda paths, etc.) is lost.User-side workarounds tried and failed
.zshrc([[ ! -t 0 ]] && return) — works in manual tests, no effect in Electron context.zprofilesource in.zshenv— shell builds correct PATH, but snapshot overwrites itlaunchctl setenv PATH— PATH specifically is overridden by the snapshot (other env vars work fine)settings.jsonenv PATH — works for VS Code extension, ignored by DesktopSuggested fixes (app-side)
-iflag:zsh -l -cinstead ofzsh -l -i -cNo activity for several weeks, closing out.
I also have this issue with claude desktop claude code
Having this issue today, on 1.9659.0
Same issue. Claude 1.9659.4 (8cc655) 2026-06-02T02:30:57.000Z
Data point from Windows that may help narrow this down: the same error message has (at least) two distinct root causes behind it, and the detection code makes both undiagnosable from the outside.
I analyzed the bundled code of Claude Code 2.1.205/2.1.206 (details in my comment on #23704). The PDF probe runs
pdftoppm -vand treats poppler as available iffcode === 0 || (code !== 127 && stderr.length > 0). Two problems:preserveOutputOnError: true, so wheneverpdftoppm -vexits non-zero, stdout/stderr are discarded before the check runs — thestderr.length > 0branch can never trigger. Detection silently reduces to "spawn must succeed and exit 0".where.exe-based): one transient resolution failure is cached as "not found" for the whole process lifetime, which produces this exact error even whenPATHis perfectly fine (that's the failure mode in #42330 and the "not detected after install" symptom in #23704).For the desktop-app case in this issue the trigger is presumably the minimal
PATHof the app-launched process (no/opt/homebrew/bin//usr/local/bin), since on macOS the probe spawnspdftoppmwith the process environment as-is. But the fixes overlap:pdftoppmthe same way BashTool resolves the user's shell environment (login-shell / shell-snapshotPATH), or honor an explicit override (env var or setting) pointing at the binary.preserveOutputOnError: truein the probe so partially working setups are at least detected.PATHthat was searched in the error message instead of the generic "pdftoppm is not installed" — that would have made all three of these issues self-explanatory for reporters.Cross-referencing #23704 and #42330, which share the error message but have the Windows-specific cache as root cause.