<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sarthak Kundu</title>
    <description>The latest articles on DEV Community by Sarthak Kundu (@itsdrvgo).</description>
    <link>https://dev.to/itsdrvgo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4240337%2F2fa0b8e5-802b-44ab-a4be-584cef0e653d.jpg</url>
      <title>DEV Community: Sarthak Kundu</title>
      <link>https://dev.to/itsdrvgo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/itsdrvgo"/>
    <language>en</language>
    <item>
      <title>Headless Chromium hung only on its second launch: pipe vs WebSocket under Bun</title>
      <dc:creator>Sarthak Kundu</dc:creator>
      <pubDate>Thu, 12 Nov 2026 06:20:38 +0000</pubDate>
      <link>https://dev.to/itsdrvgo/headless-chromium-hung-only-on-its-second-launch-pipe-vs-websocket-under-bun-3ii1</link>
      <guid>https://dev.to/itsdrvgo/headless-chromium-hung-only-on-its-second-launch-pipe-vs-websocket-under-bun-3ii1</guid>
      <description>&lt;p&gt;Invoice PDFs that sometimes never finished rendering. That was the bug. It took far longer to find than to fix, and the cause was strange enough that I want to write it down.&lt;/p&gt;

&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;I'm building &lt;a href="https://freveo.app?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=k05" rel="noopener noreferrer"&gt;Freveo&lt;/a&gt;, a workspace for freelancers: proposals, invoices, change orders, time tracking. Every invoice, contract and timesheet is rendered to PDF in headless Chromium, in a separate PDF service. The API runs on Bun.&lt;/p&gt;

&lt;h2&gt;
  
  
  The symptom
&lt;/h2&gt;

&lt;p&gt;Renders sometimes never finished. I first saw it in CI, once the new change-order tests started running beside the invoice tests. Nothing failed with an error. A render just sat there until the test runner gave up.&lt;/p&gt;

&lt;p&gt;Locally it was hard to trigger, which is the worst kind of bug to have.&lt;/p&gt;

&lt;h2&gt;
  
  
  The wrong fix
&lt;/h2&gt;

&lt;p&gt;My first move was a longer test timeout. It was the obvious thing: slow CI, long PDF, give it more time.&lt;/p&gt;

&lt;p&gt;It didn't help. The hang wasn't slow, it was permanent. I reverted the change, because a timeout that hides a hang is how you end up shipping one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;Once I stopped guessing and started logging, the pattern was narrow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only a Chromium launched &lt;strong&gt;after an earlier one had closed&lt;/strong&gt;, in the &lt;strong&gt;same process&lt;/strong&gt;, stalled.&lt;/li&gt;
&lt;li&gt;The first launch was always fine.&lt;/li&gt;
&lt;li&gt;The stall happened on one call: &lt;code&gt;setContent&lt;/code&gt; with a large document, multiple megabytes of HTML. Small pages were fine.&lt;/li&gt;
&lt;li&gt;It stalled below Puppeteer's own timeouts, so nothing threw. It simply waited.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's why it only showed up when tests ran together: the invoice tests closed a browser, and the next suite launched a new one in the same Bun process.&lt;/p&gt;

&lt;h2&gt;
  
  
  The proof
&lt;/h2&gt;

&lt;p&gt;"It seems to be the second launch" isn't a diagnosis, so I made a reproduction I could run on demand: a linux/amd64 container, the same large &lt;code&gt;setContent&lt;/code&gt;, a first browser opened and closed, then a second one launched.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connected over the &lt;strong&gt;pipe&lt;/strong&gt;: it hung on every run.&lt;/li&gt;
&lt;li&gt;Connected over a &lt;strong&gt;WebSocket&lt;/strong&gt;: it never hung.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same code, same page, same container. The only variable was how the process talked to Chromium. That was the point where I stopped suspecting my own code.&lt;/p&gt;

&lt;p&gt;I'm not claiming a root cause deeper than that. I can tell you what reproduces and what doesn't. Why the pipe transport stalls on a large write in a second browser under Bun, I can't say with certainty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it mattered in production
&lt;/h2&gt;

&lt;p&gt;In CI this was an annoyance. In production it was worse.&lt;/p&gt;

&lt;p&gt;The PDF service recycles browsers and kills renders that pass their deadline, and both relaunch Chromium inside a process that has already closed one. So a render could hang until the container's watchdog noticed and restarted the whole service. A customer waiting on an invoice PDF doesn't care about transports.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;The fix was small: connect to Chromium over WebSocket instead of the pipe. In Puppeteer terms, that means not launching with &lt;code&gt;pipe: true&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before: stalled on the second launch under Bun&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// After: default WebSocket connection&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The container reproduction became a regression check, so if someone changes the transport later, it fails loudly instead of hanging quietly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd check first next time
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Does it only happen after a previous instance closed?&lt;/strong&gt; Ordering bugs hide in "works on first run" code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does it depend on payload size?&lt;/strong&gt; A hang that only appears with large input points at buffering or transport, not logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can I change the transport and keep everything else identical?&lt;/strong&gt; That single swap told me more than hours of staring at my own code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproduce in the same OS and architecture as production.&lt;/strong&gt; The container was what made it deterministic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be suspicious of timeouts as fixes.&lt;/strong&gt; If a longer wait changes nothing, you don't have a slow problem.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  One more thing
&lt;/h2&gt;

&lt;p&gt;If you've hit something similar with Puppeteer or Playwright under Bun (or Node), I'd like to know whether the second-launch stall reproduces for you too. What's the strangest bug that only ever showed up in CI for you?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I'm building Freveo solo and writing up the bugs along the way.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>bunjs</category>
      <category>puppeteer</category>
      <category>debugging</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
