Desktop app (macOS) ignores PATH from all sources — Read tool cannot find poppler/pdftoppm

Resolved 💬 25 comments Opened Apr 1, 2026 by thatChadM Closed May 18, 2026

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

  1. Install poppler: brew install poppler
  2. Verify it works: /opt/homebrew/bin/pdftoppm -v returns pdftoppm version 26.03.0
  3. Open Claude Desktop app
  4. Ask Claude to read any PDF file using the Read tool
  5. Result: pdftoppm is not installed. Install poppler-utils...
  6. Ask Claude to run echo "$PATH" | tr ':' '\n' via Bash tool
  7. 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 poppler installs 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 setenv ARE visible to the Desktop app (confirmed by checking custom env vars via Bash set command). Only PATH appears to be overridden.
  • Result: Not found — PATH specifically is being overridden by the Desktop app

5. Shell snapshots

  • ~/.claude/shell-snapshots/*.sh contains the full PATH with /opt/homebrew/bin and /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)

View original on GitHub ↗

25 Comments

github-actions[bot] · 3 months ago

Found 1 possible duplicate issue:

  1. https://github.com/anthropics/claude-code/issues/42203

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

CharlieOktopous · 3 months ago
## 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 1. Install poppler: brew install poppler 2. Verify it works: /opt/homebrew/bin/pdftoppm -v returns pdftoppm version 26.03.0 3. Open Claude Desktop app 4. Ask Claude to read any PDF file using the Read tool 5. Result: pdftoppm is not installed. Install poppler-utils... 6. Ask Claude to run echo "$PATH" | tr ':' '\n' via Bash tool 7. 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 poppler installs 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 setenv ARE visible to the Desktop app (confirmed by checking custom env vars via Bash set command). Only PATH appears to be overridden. Result: Not found — PATH specifically is being overridden by the Desktop app ### 5. Shell snapshots ~/.claude/shell-snapshots/*.sh contains the full PATH with /opt/homebrew/bin and /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 [[BUG] Code tab fails with SSL error: desktop app does not forward NODE_EXTRA_CA_CERTS to CLI subprocess #22559](https://github.com/anthropics/claude-code/issues/22559) — Desktop app does not forward env vars to CLI subprocess (documents the "utility process" PATH extraction) [[BUG] NODE_EXTRA_CA_CERTS is not effective when set in ~/.claude/settings.json #22512](https://github.com/anthropics/claude-code/issues/22512) — settings.json env vars not applied to CLI subprocess (OPEN) [[BUG] Read tool's PDF support requires poppler-utils but it's undocumented, usually absent, and not detected after install #23704](https://github.com/anthropics/claude-code/issues/23704) — Read tool PDF support requires undocumented poppler-utils Read tool cannot find poppler-utils in headless (-p) mode #29886 — Read tool can't find poppler in headless mode [[FEATURE] Allow users to configure which shell the Bash tool uses or inherit initial shell #7490](https://github.com/anthropics/claude-code/issues/7490) — Shell configuration not inherited by Bash tool (91 upvotes) * Shell snapshot PATH export contains literal $PATH — system directories lost #31799 — Mentions settings.json env PATH as fix (works for CLI/VS Code, not Desktop)
CharlieOktopous · 3 months ago

The same PATH stripping by the disclaimer binary also breaks the Claude Preview tool when using Next.js with Turbopack.

What happens: The preview server spawns node via the absolute path in launch.json, which works. But Turbopack (Rust-based) internally spawns node as a child process for PostCSS/CSS processing via its "node pooled process" worker pool. Since it resolves node from PATH, it fails:

e0da · 3 months ago

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.

pawel-omniaz · 3 months ago

I have this issue too.

druvv · 3 months ago

Someone please fix. I can't use Claude Desktop at the moment and I really want to experiment with the preview tooling!

codingman7778 · 3 months ago

@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..

simonsmallchua · 3 months ago

This breaks everything, not just pdftoppm. many tools not accessible.

thatChadM · 3 months ago
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.

mahoor · 3 months ago

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.

thatChadM · 3 months ago

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.

thatChadM · 3 months ago

Saving this chunk of my CLAUDE.md for reference in case this bug resurfaces & the workaround is necessary again:

## Desktop App Known Issues
- Check `CLAUDE_CODE_ENTRYPOINT` env var to confirm Desktop (`claude-desktop`) vs VS Code (`claude-vscode`).

### PDF Read tool — FIXED as of Claude 1.1062.0
- **Status:** Fixed as of Claude 1.1062.0 (b81bcd) 2026-04-06T21:20:28.000Z
- Just use the Read tool directly with the `pages` parameter for large PDFs. The bash workaround below is no longer required.
- Original bug reference: anthropics/claude-code#42248

### PDF workaround (HISTORICAL — kept in case the bug returns)
The following was the workaround used before the fix. Preserved for reference:
- **Workaround:** Use Bash instead of Read for PDFs. Cache everything in `/tmp/claude-pdf/<sanitized-filename>/`:
  - **FIRST: check if already extracted** — `ls /tmp/claude-pdf/<name>/` before re-extracting
  - **Text (full document, searchable):**
    ```
    mkdir -p /tmp/claude-pdf/<name>
    /opt/homebrew/bin/pdftotext "<pdf_path>" /tmp/claude-pdf/<name>/full.txt
    ```
    Then Read or Grep the .txt file. One file, all pages, searchable.
  - **Visual (per-page PNGs for diagrams, schematics, tables):**
    ```
    /opt/homebrew/bin/pdftoppm -png -r 200 -f <first> -l <last> "<pdf_path>" /tmp/claude-pdf/<name>/page
    ```
    Then Read the .png files. Render only the pages you need.
  - Use text for searching specs, PNG for visual inspection. Both persist for re-reading.
  - PNG only for images — PPM and TIFF are too large for Read tool (~6MB/page vs ~400KB)
- **Other Homebrew tools previously affected:** The PATH bug affected ALL Homebrew-installed binaries, not just poppler. If the bug returns, use full paths for any Homebrew tool:
  - `gh` (GitHub CLI): `/opt/homebrew/bin/gh`
  - `pdftotext`/`pdftoppm`/`pdfinfo`: `/opt/homebrew/bin/` prefix
  - Any other Homebrew tool: check with `ls /opt/homebrew/bin/<tool>`
aosq · 3 months ago

The desktop fix does now include more paths in $PATH and some command are now working, but it still uses its own custom $PATH with a stripped down list of paths:

/usr/bin
/bin
/usr/sbin
/sbin
/usr/local/bin
/opt/homebrew/bin
~/Library/Application Support/Claude/local-agent-mode-sessions/...
~/.claude/plugins/cache/claude-plugins-official/...

For mean, this means that Claude Code on desktop still misses uv (~/.local/bin/uv) and bun (~/.bun/bin/bun). Where as Claude Code CLI starts with my full configured $PATH (which includes ~/.local/bin and ~/.bun/bin).

thatChadM · 3 months ago

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.

aosq · 3 months ago
that means that environment variables (like PATH) that are set in shell profiles are never, and will never, picked up automatically on typical app start so building a default PATH list makes sense.

This may be the case, but it's also true that previously (at least for myself) uv and bun where available to Claude Code Desktop without any extra settings/config monkey-ing around to update $PATH with uv and bun'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.)

thatChadM · 3 months ago
This may be the case, but it's also true that previously (at least for myself) uv and bun where available to Claude Code Desktop without any extra settings/config monkey-ing around to update $PATH with uv and bun's paths.

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.

thatChadM · 3 months ago

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:

06:08:07 [warn] [CCD] Spawn PATH missing Homebrew bin — applied allPaths() floor 
(added: /usr/local/bin, /opt/homebrew/bin, /opt/local/bin). Indicates shell PATH 
extraction returned an incomplete PATH for this user.

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.

aosq · 3 months ago

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" />

thatChadM · 3 months ago

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.

furechan · 3 months ago

Root Cause Analysis

I've traced this to two specific mechanisms in the Desktop app:

1. PATH extraction uses -i (interactive) flag

The Electron app extracts PATH at launch by spawning:

zsh -l -i -c 'echo "___CLAUDE_PATH_EXTRACT___$PATH"'

The -i flag 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 -c instead of zsh -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:

export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/homebrew/bin:...'

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.json env — the snapshot clobbers them all.

Why /opt/homebrew/bin survives but nothing else

Homebrew installs /etc/paths.d/Homebrew, so /opt/homebrew/bin is 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

  • TTY guard in .zshrc ([[ ! -t 0 ]] && return) — works in manual tests, no effect in Electron context
  • Conditional .zprofile source in .zshenv — shell builds correct PATH, but snapshot overwrites it
  • launchctl setenv PATH — PATH specifically is overridden by the snapshot (other env vars work fine)
  • settings.json env PATH — works for VS Code extension, ignored by Desktop

Suggested fixes (app-side)

  1. Drop the -i flag: zsh -l -c instead of zsh -l -i -c
  2. Alternatively, read PATH from the spawned shell's environment rather than hardcoding the extraction result into the snapshot
thatChadM · 2 months ago

No activity for several weeks, closing out.

zachmayer · 1 month ago

I also have this issue with claude desktop claude code

cjonesde · 1 month ago

Having this issue today, on 1.9659.0

JackChen-me · 1 month ago

Same issue. Claude 1.9659.4 (8cc655) 2026-06-02T02:30:57.000Z

Robertstuffers · 6 days ago

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 -v and treats poppler as available iff code === 0 || (code !== 127 && stderr.length > 0). Two problems:

  1. The probe swallows its own fallback (all platforms, including macOS). The exec helper is invoked without preserveOutputOnError: true, so whenever pdftoppm -v exits non-zero, stdout/stderr are discarded before the check runs — the stderr.length > 0 branch can never trigger. Detection silently reduces to "spawn must succeed and exit 0".
  2. On Windows there's additionally a sticky negative cache in the executable resolver (where.exe-based): one transient resolution failure is cached as "not found" for the whole process lifetime, which produces this exact error even when PATH is 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 PATH of the app-launched process (no /opt/homebrew/bin / /usr/local/bin), since on macOS the probe spawns pdftoppm with the process environment as-is. But the fixes overlap:

  • Resolve pdftoppm the same way BashTool resolves the user's shell environment (login-shell / shell-snapshot PATH), or honor an explicit override (env var or setting) pointing at the binary.
  • Pass preserveOutputOnError: true in the probe so partially working setups are at least detected.
  • Include the underlying spawn/resolution error and the PATH that 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.
  • Document poppler as the optional dependency for PDF page rendering.

Cross-referencing #23704 and #42330, which share the error message but have the Windows-specific cache as root cause.