Skip to content

fix(make-pdf): resolve browse binary on Windows without BROWSE_BIN#1094

Open
BkashJEE wants to merge 1 commit intogarrytan:mainfrom
BkashJEE:fix/make-pdf-windows-browse-resolution
Open

fix(make-pdf): resolve browse binary on Windows without BROWSE_BIN#1094
BkashJEE wants to merge 1 commit intogarrytan:mainfrom
BkashJEE:fix/make-pdf-windows-browse-resolution

Conversation

@BkashJEE
Copy link
Copy Markdown

Summary

make-pdf/src/browseClient.ts resolveBrowseBin() hardcoded the Unix binary name browse and the Unix which lookup tool. On Windows the compiled binary is browse.exe, so every fallback (sibling paths, global install at ~/.claude/skills/gstack/browse/dist/browse, PATH lookup) missed — users hit browse binary not found unless they set BROWSE_BIN manually.

This PR makes every fallback Windows-aware while keeping Unix behavior unchanged.

Changes

  • Branch binary name on process.platform === "win32" → append .exe.
  • Use where on Windows, which on Unix; parse first line of where output (can span multiple PATHEXT hits).
  • isExecutable() checks F_OK on Windows — NTFS has no execute bit and Node's X_OK delegates to AccessCheck, which false-negatives on .exe files depending on ACLs.
  • Error message now shows the actual binary name searched and suggests setx BROWSE_BIN ... on Windows instead of export.

Verification

Environment: Windows 11 Pro + Git Bash + gstack v1.4.0.0 (main).

Before patch: with BROWSE_BIN unset, pdf.exe generate smoke.md out.pdf fails:

$P: browse newtab exited 1: ...

Root cause: fs.accessSync("~/.claude/skills/gstack/browse/dist/browse", X_OK) throws ENOENT because the actual file is browse.exe.

After patch: same command succeeds. Binary resolved via the global-install branch. Generated a 51KB single-page PDF end-to-end. Full PDF pipeline validated (cover + TOC + paged.js render + pdftotext QA) in a separate run against ~/.claude/CLAUDE.md (10 pages, 208KB).

No platform-conditional test added — the change is a pure cross-platform branching patch with the same semantics on macOS/Linux (empty suffix, which, X_OK).

Test plan

  • Windows: pdf.exe generate with BROWSE_BIN unset succeeds
  • Windows: error message mentions browse.exe and setx when binary truly missing
  • macOS/Linux: no behavior change (empty suffix, which, X_OK preserved — request reviewer run existing test suite)

Context

Discovered while dogfooding /make-pdf on Windows the day after the v1.4.0.0 release. Without this fix, the skill is effectively blocked on Windows until users know to run setx BROWSE_BIN "C:\path\to\browse.exe" themselves — not documented in SKILL.md.

…_BIN

The binary resolution chain in `make-pdf/src/browseClient.ts` hardcoded
the Unix binary name `browse` and the Unix lookup tool `which`. On Windows
the compiled binary is `browse.exe`, so every fallback (sibling paths,
global install at `~/.claude/skills/gstack/browse/dist/browse`, PATH
lookup) missed, and users hit a cryptic "browse binary not found" error
unless they knew to set `BROWSE_BIN` manually.

Changes:
- Branch binary name on `process.platform === "win32"` → append `.exe`.
- Use `where` on Windows, `which` on Unix; parse first line of `where`
  output (can be multi-line across PATHEXT hits).
- `isExecutable()` checks `F_OK` on Windows because NTFS has no execute
  bit and `X_OK` is unreliable (Node delegates to `AccessCheck` which
  can false-negative on .exe files depending on ACLs).
- Error message now shows the actual binary name being searched and
  suggests `setx BROWSE_BIN ...` on Windows instead of `export`.

Verified on Windows 11 + Git Bash:
- Before patch: `pdf.exe generate ...` fails with "browse binary not
  found" unless `BROWSE_BIN` is set explicitly via `setx`.
- After patch: succeeds with empty `BROWSE_BIN`, finds
  `~/.claude/skills/gstack/browse/dist/browse.exe` via the global path
  branch. Rendered a 51KB smoke-test PDF end-to-end.

No behavior change on macOS/Linux (suffix is empty string, lookup tool
stays `which`, permission check stays `X_OK`).
@nehaaprasad
Copy link
Copy Markdown

AI Code Trust — ship readiness

Score: 100 · Verdict: SAFE · Model: deterministic-v1

No blocking issues detected by automated checks.

scarson added a commit to scarson/gstack that referenced this pull request Apr 21, 2026
Adopt the platform-aware error-message UX from @BkashJEE's independent
concurrent fix (garrytan#1094). On Windows, `export FOO=...` is the wrong
syntax — users want `setx FOO "..."` for a persistent environment
variable in cmd.exe/PowerShell, or `$env:FOO = "..."` for session-local
PowerShell. `setx` is the closest one-liner equivalent and matches what
Bikash proposed.

Also adds `Platform: <process.platform>` to both error messages — tiny
but useful for bug reports, especially given both fix sites (browse and
pdftotext) have subtle platform-specific behavior.

Credit: @BkashJEE for the `setx` hint shape in garrytan#1094.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@scarson
Copy link
Copy Markdown

scarson commented Apr 21, 2026

Hey @BkashJEE — just noticed this after I'd already prepped a fix for the same bug at #1118. You're the first-mover on the diagnosis by a day, and the setx BROWSE_BIN "..." vs export BROWSE_BIN=... hint in your error message was a genuinely better UX choice than what I had. I just amended my branch to adopt that phrasing, with a credit line in the commit.

Cross-linking for the reviewer's convenience. Our two PRs overlap on the core diagnosis (Node's fs.accessSync(X_OK) degrades to existence-check on Windows; bun --compile --outfile emits .exe; which isn't available outside Git Bash so where is needed) and the shape of the fix (probe .exe suffix, branch where/which by platform, clean up the error path).

Mine goes a bit wider on scope: applies the same fix to pdftotext.ts (same root cause, different binary resolver), adds regression tests, and fixes a pre-existing hardcoded-/bin/sh test that would have blocked Windows CI enablement. Happy either way — this is @garrytan's call on consolidation shape. The important thing is that Windows users stop needing the BROWSE_BIN workaround.

Nice catch on the diagnosis.

@scarson
Copy link
Copy Markdown

scarson commented Apr 21, 2026

For context: opened #1122 today ("Boiling the Windows CI Lake") proposing a Phase-1 Windows smoke CI that would have caught this class of bug at PR time. This PR is cited in the RFC's coverage table as one of the five recent examples that motivated it — not asking for anything on this PR, it can merge on its own merits. Cross-linking because the "Why this shipped unnoticed" paragraph above reads differently with a systemic answer in flight.

Also a companion issue at #1123 for higher-level discussion if the PR's review thread gets crowded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants