Skip to content

fix(pdf): make write_pdf Chrome acquisition self-healing and fail clearly#550

Open
wonderwhy-er wants to merge 1 commit into
mainfrom
fix/pdf-chrome-autofetch-recovery
Open

fix(pdf): make write_pdf Chrome acquisition self-healing and fail clearly#550
wonderwhy-er wants to merge 1 commit into
mainfrom
fix/pdf-chrome-autofetch-recovery

Conversation

@wonderwhy-er

@wonderwhy-er wonderwhy-er commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Problem

write_pdf could fail with an opaque "Failed to launch the browser process" on machines without system Chrome — even though the codebase does have logic to download Chrome automatically. Investigation on a Windows box (with the Puppeteer-pinned Chrome build removed to simulate a fresh machine) traced it to three combining issues:

  1. Build/cache mismatch. installChrome() downloaded the stable Chrome-for-Testing build into Desktop Commander's private cache, while md-to-pdf's bundled Puppeteer looks for a different, pinned build in the default cache (~/.cache/puppeteer). The two resolution paths never aligned, so DC's own install could never satisfy the fallback.
  2. Sticky failure. On any install failure, getChromePath() cached undefined for the entire process lifetime. A single transient failure (offline, proxy, locked-down network) permanently disabled PDF generation until restart — no retry.
  3. Silent broken fallback. When getChromePath() returned undefined, parseMarkdownToPdf silently omitted executablePath and let md-to-pdf fall through to bundled Puppeteer, which hunts for the pinned build in a cache DC never populates → the opaque launch error the user actually sees.

The auto-fetch mechanism itself works (verified: downloads a working Chrome in ~19s). The failures came from these three gaps around it.

Fixes (all in src/tools/pdf/markdown.ts)

  • Install the pinned build. installChrome() now resolves the Chrome build pinned by the co-bundled puppeteer-core (via PUPPETEER_REVISIONS), falling back to the stable channel if the pin can't be read. The downloaded browser is now guaranteed compatible with the launcher and lives where it's expected.
  • Self-healing retry. Stop caching undefined on install failure. Leaving the cache unset lets the next write_pdf retry the download instead of short-circuiting forever.
  • Actionable error. Replace the silent bundled-Puppeteer fallback with a clear error that names the underlying install failure reason, instead of the opaque "Failed to launch the browser process".

Verification

  • Auto-fetch downloads a working Chrome (~19s) and now targets the pinned build (confirmed the resolver returns the co-bundled puppeteer-core pin rather than drifting stable).
  • npm run build compiles clean (no TS errors).
  • read_file/write_file/edit_block/list_directory/PDF read + write all continue to work on Windows.

Notes

  • No behavior change on the happy path (system Chrome present, or Chrome already cached): DC still passes an explicit executablePath and PDFs generate as before.
  • This is independent of the v0.2.43 → next release content; it fixes a latent packaging/robustness issue in PDF creation.

Summary by CodeRabbit

  • Bug Fixes
    • Improved PDF export reliability when Chrome is unavailable.
    • PDF generation now returns a clearer error message with more detail about launch or install failures.
    • Browser detection can retry after a failed install instead of permanently treating Chrome as missing.
    • Chrome selection now more consistently uses the expected pinned version, with a fallback to the stable channel when needed.

…arly

write_pdf could fail on Windows (and any OS) with an opaque "Failed to
launch the browser process" on machines without system Chrome. Three
issues combined:

1. installChrome() downloaded the 'stable' Chrome-for-Testing build into
   DC's private cache, while md-to-pdf's bundled Puppeteer looks for a
   different PINNED build in the default cache. The two never aligned.
2. On any install failure, getChromePath() cached `undefined` for the
   whole process, so a single transient failure (offline/proxy/locked-down
   network) permanently disabled PDF generation until restart.
3. When getChromePath() returned undefined, parseMarkdownToPdf silently
   fell through to md-to-pdf's bundled Puppeteer, which resolves the pinned
   build in a cache DC never populates -> opaque launch error.

Fixes:
- Install the Chrome build PINNED by the co-bundled puppeteer-core (falls
  back to 'stable' if the pin can't be read), so the downloaded browser is
  guaranteed compatible with the launcher and lives where it's expected.
- Do not cache `undefined` on install failure; leave the cache unset so the
  next write_pdf retries the download.
- Replace the silent bundled-Puppeteer fallback with an actionable error
  that includes the underlying install failure reason.

Verified: auto-fetch downloads a working Chrome (~19s) and now targets the
pinned build; project builds clean.
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 77ce6930-64e5-4323-9824-2da867a0bb3a

📥 Commits

Reviewing files that changed from the base of the PR and between 9c15e1c and 5094120.

📒 Files selected for processing (1)
  • src/tools/pdf/markdown.ts

📝 Walkthrough

Walkthrough

Modifies Chrome resolution logic in the PDF markdown tool: adds tracking of the last Chrome installation error, prefers a pinned puppeteer-core Chrome revision during installation with stable-channel fallback, avoids permanently caching install failures, and throws an actionable error while unconditionally wiring executablePath in parseMarkdownToPdf.

Changes

Chrome Resolution and Error Handling

Layer / File(s) Summary
Track and reuse last Chrome install error
src/tools/pdf/markdown.ts
Adds a module-level lastChromeError variable and updates getChromePath to record and log installation failures without permanently caching an undefined result, allowing retries.
Prefer pinned puppeteer-core Chrome revision
src/tools/pdf/markdown.ts
Updates build ID resolution during Chrome installation to try the pinned puppeteer-core revision first, falling back to the stable channel build ID.
Actionable error and executablePath wiring
src/tools/pdf/markdown.ts
parseMarkdownToPdf now throws a detailed error (including the last recorded Chrome error) when no path is resolved, and always sets launch_options.executablePath from the resolved path.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant parseMarkdownToPdf
  participant getChromePath
  participant lastChromeError

  Caller->>parseMarkdownToPdf: request PDF generation
  parseMarkdownToPdf->>getChromePath: resolve Chrome executable
  getChromePath->>getChromePath: attempt install / lookup
  alt install fails
    getChromePath->>lastChromeError: store error
    getChromePath-->>parseMarkdownToPdf: return undefined
    parseMarkdownToPdf->>lastChromeError: read last error
    parseMarkdownToPdf-->>Caller: throw actionable error
  else path resolved
    getChromePath-->>parseMarkdownToPdf: return chromePath
    parseMarkdownToPdf->>parseMarkdownToPdf: set launch_options.executablePath
    parseMarkdownToPdf-->>Caller: continue PDF generation
  end
Loading

Possibly related PRs

  • wonderwhy-er/DesktopCommanderMCP#306: Both PRs modify the Chrome resolution/installation flow in src/tools/pdf/markdown.ts and wire the resolved Chrome path into parseMarkdownToPdf error handling.
  • wonderwhy-er/DesktopCommanderMCP#485: Both PRs change how cached Chrome executables are found/returned and how installation/caching failures are handled in the same file.

Suggested labels: size:M

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main PDF Chrome acquisition and failure-handling changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/pdf-chrome-autofetch-recovery

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

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.

1 participant