{"slug": "a-stranger-fixed-my-bug-then-i-found-out-he-fixed-the-wrong-half-and-it-still", "title": "A stranger fixed my bug. Then I found out he fixed the wrong half — and it still worked.", "summary": "A developer discovered that a pull request to their Safari MCP server fixed a real bug, but half of the patch was unnecessary. The actual fix addressed a subtle concurrency bug where a write failure could desynchronize the request-response queue, causing cascading hangs. The developer built a model to understand the issue and found that the timeout path was safe, but the write-failure path left a callback in the queue that could consume a future reply.", "body_md": "Someone opened a pull request against my Safari MCP server last week. Three functions, same treatment each, clean write-up, all CI green. It fixed a real bug — his repro was solid, mine reproduced it too.\n\nThen I sat down to write it up, built a 40-line model of the thing to make sure I understood it, and discovered that **half of his patch does nothing at all.** Not \"does something subtle.\" Nothing. The two hunks he described as the fix are behaviorally identical to the code they replace.\n\nThe half he *didn't* emphasize is the one that closes the bug. And the reason neither of us could tell — the reason I couldn't tell about my own code — turns out to be the actual story here.\n\nMy server talks to a small Swift helper for the things JavaScript can't do on a Mac: focus an app, hide a window, synthesize a real OS-level click. Newline-delimited JSON over stdin/stdout. Request and response are correlated **positionally** — there are no request IDs:\n\n``` js\nconst _helperQueue = []; // callbacks waiting for responses\n\n_helperProc.stdout.on(\"data\", (chunk) => {\n  _buf += chunk.toString();\n  const lines = _buf.split(\"\\n\");\n  _buf = lines.pop();\n  for (const line of lines) {\n    if (!line.trim()) continue;\n    const cb = _helperQueue.shift();   // first reply belongs to first waiter\n    if (cb) cb(line);\n  }\n});\n```\n\nThis works exactly as long as one invariant holds:\n\nEvery callback in the queue corresponds to a request that was actually sent and whose reply is still coming.\n\nViolate that once and the queue is off by one. Reply N goes to waiter N+1. Forever — there is nothing in the protocol that can ever resynchronize it. And because every waiter is a `setTimeout`\n\n-guarded promise, the symptom is never an error. It's a hang.\n\nA helper call looks like this (this is the real pre-fix code, lightly trimmed):\n\n``` js\nfunction _helperGetFrontApp(timeout = 2000) {\n  return _withHelperLock(() => new Promise((resolve) => {\n    let resolved = false;\n\n    const timer = setTimeout(() => {\n      if (!resolved) { resolved = true; resolve(null); }        // path 1: gave up waiting\n    }, timeout);\n\n    function cb(line) {\n      if (resolved) return;\n      resolved = true;\n      clearTimeout(timer);\n      resolve(line.trim());\n    }\n\n    _helperQueue.push(cb);                                       // ← pushed BEFORE the write\n    try { _helperProc.stdin.write('{\"getFrontApp\":true}\\n'); }\n    catch { clearTimeout(timer); resolve(null); }                // path 2: write blew up\n  }));\n}\n```\n\nThere are two ways to walk away from a request, and they are **not** the same, which is the whole point:\n\n**Path 1 — the timeout.** The helper is slow; we stop waiting. But a reply is still coming. The dead `cb`\n\nstays in the queue, gets shifted when the late line lands, sees `resolved === true`\n\n, and returns without touching anything. It ate exactly one line — which is precisely correct, because exactly one line was owed. **The queue stays aligned.**\n\n**Path 2 — the write failure.** `stdin.write`\n\nthrows (broken pipe: the helper died between our last call and this one). The request was **never sent**, so no reply is ever coming. But look at the `catch`\n\n: it calls `resolve(null)`\n\nand **never sets resolved = true.** So\n\n`cb`\n\nis still sitting in the queue, and it's still The next request comes in. Its reply arrives. `shift()`\n\nhands it to the armed corpse of a request that was never sent. `resolved`\n\nis false, so it consumes the line, resolves a promise nobody is listening to, and **the caller who actually sent a request gets nothing** — it waits out its full timeout, and leaves its own callback behind when it does.\n\nThat's not an off-by-one. That's a cascade. One failed write poisons the channel for the lifetime of the daemon.\n\nI've been wrong about my own concurrency code before, so I stopped arguing with myself and modeled it — a positional queue, the same `resolved`\n\nguard, the two abandonment paths, pre-fix and post-fix:\n\n```\nPRE-FIX   timeout   → B got: reply-to-B     ← already correct!\nPRE-FIX   writefail → B got: TIMEOUT(B)     ← B's reply eaten by A's armed callback\nPOST-FIX  timeout   → B got: reply-to-B\nPOST-FIX  writefail → B got: reply-to-B     ← fixed\n```\n\nThere it is. The timeout path was **never broken.** The patch's headline change — replacing the abandoned callback with an explicit no-op consumer:\n\n``` js\nconst idx = _helperQueue.indexOf(cb);\nif (idx >= 0) _helperQueue[idx] = () => {};   // no-op consumer for the late reply\n```\n\n...replaces a callback that was *already* a no-op consumer (because `cb`\n\nself-guards on `resolved`\n\n) with a callback that is *visibly* a no-op consumer. Zero behavior change. It's a comment that happens to compile.\n\nThe fix is the other hunk, the quiet one in the `catch`\n\n:\n\n``` js\ncatch {\n  const idx = _helperQueue.indexOf(cb);\n  if (idx >= 0) _helperQueue.splice(idx, 1);   // never sent → nothing is coming → drop the slot\n  clearTimeout(timer);\n  resolve(null);\n}\n```\n\nAnd note that the two hunks do **opposite** things — one *keeps* the slot, one *removes* it — for reasons that are entirely non-obvious unless you have the positional invariant in your head. Swap them and you've built a worse bug than the one you set out to fix: `splice`\n\non timeout means the late reply gets shifted onto the next caller, who now gets someone else's answer and believes it. He got that distinction right in both directions, on his first try, in code he didn't write. That's the part I'm actually impressed by.\n\nHere's what I found when I grepped my own file for this pattern. Seven helper functions. Every one of them open-codes the same request/timeout/queue dance. Four carry some version of the no-op-consumer line:\n\n``` js\n764:  if (idx >= 0) _helperQueue[idx] = () => { _helperConsecutiveTimeouts = 0; }; // late reply ⇒ alive\n825:  if (idx >= 0) _helperQueue[idx] = () => {};   // no-op consumer for a late reply\n863:  if (idx >= 0) _helperQueue[idx] = () => {};   // No-op consumer for late response\n912:  if (idx >= 0) _helperQueue[idx] = () => {};\n```\n\nThree don't. Those three are exactly the three he patched.\n\nSo the honest reading isn't \"I forgot the fix in three places.\" It's worse and more interesting: **I wrote a defensive line four times without ever writing down what it defends against**, and in the three functions where I skipped it, the code was *accidentally fine anyway* — while a genuinely broken path sat two lines below, in every single one of the seven.\n\nThe invariant — *an abandoned slot must consume exactly one reply, unless nothing was ever sent* — is stated in exactly zero of those seven functions. Not in a comment, not in a name, not in a type. It lives only in whatever I happened to be holding in my head on the afternoon I wrote each one. A contributor reading this file has no way to check his patch against the rule, because the rule isn't there. He had to reconstruct it from the wreckage — and he reconstructed it correctly, which is why his `catch`\n\nhunk lands even though his description of *why* doesn't.\n\nThat's the failure. Not the missing line. The missing **place to put the line.**\n\nI don't care much about DRY as an aesthetic. A little duplication beats a bad abstraction, and I'll take three copies of a five-line function over an inheritance hierarchy every day of the week.\n\nBut that argument is about *code you can see*. This is about something else:\n\nA rule that exists in N copies has to be\n\nre-derived from scratchby whoever writes copy N+1 — including you, next year, at 11pm.\n\nSeven functions, seven independent recollections of an unwritten rule. I got the cosmetic half right four times out of seven and the load-bearing half wrong seven times out of seven. That is not a discipline problem you fix by being more careful. It's a coin flip you have to win every time, forever, and the odds get worse with every function added.\n\nThe correct shape was always one function:\n\n```\n// The helper protocol is POSITIONAL — replies are matched to requests by queue order,\n// not by id. So an abandoned request must leave the queue in a consistent state:\n//   • timed out  → a reply IS still coming → leave a no-op consumer to eat exactly one line\n//   • write failed → nothing was ever sent → remove the slot entirely\n// Get this backwards and every subsequent reply goes to the wrong caller, silently, forever.\nfunction _helperRequest(payload, { timeout, onLine, onAbandon }) { ... }\n```\n\nSeven callers, one invariant, stated once, in the only place where it can be found by the person who needs it. Copy eight — written by someone who never read this article — gets it for free.\n\nThat refactor is mine to do, and it's the actual fix. His patch fixed the three instances. The reason there were three instances to fix is still sitting in my file.\n\n54 tests on this file. All green. Not one of them ever failed a write.\n\nOf course they didn't — you have to go out of your way to break a pipe in a test, and nobody writes that test until the bug that needs it has already shipped. The bug lived in the intersection of two things a test suite naturally avoids: the error path of the transport, and *state that outlives a single call*. My tests all assert what one request returns. The bug is only visible in what the **next** one returns.\n\nHe didn't find it by reading my code looking for bugs. He found it because `safari_new_tab`\n\nhung on his machine, and he kept going past the symptom until he hit a queue that was one slot off.\n\nDo you have a positional protocol in production — FIFO correlation, no request IDs? I want to know whether \"an abandoned slot must eat exactly one reply\" is folklore that everyone rediscovers the hard way, or whether the real lesson is just *don't build a protocol without request IDs*. Because I'm now fairly sure the invariant I'm about to carefully centralize is one I shouldn't need to have at all.\n\n*Source: achiya-automation/safari-mcp — a Safari MCP server that drives your real, logged-in browser. The PR is #53, by @jrepp.*", "url": "https://wpnews.pro/news/a-stranger-fixed-my-bug-then-i-found-out-he-fixed-the-wrong-half-and-it-still", "canonical_source": "https://dev.to/achiya-automation/a-stranger-fixed-my-bug-then-i-found-out-he-fixed-the-wrong-half-and-it-still-worked-309g", "published_at": "2026-07-12 07:16:34+00:00", "updated_at": "2026-07-12 07:43:16.718019+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence"], "entities": ["Safari MCP server", "Swift"], "alternates": {"html": "https://wpnews.pro/news/a-stranger-fixed-my-bug-then-i-found-out-he-fixed-the-wrong-half-and-it-still", "markdown": "https://wpnews.pro/news/a-stranger-fixed-my-bug-then-i-found-out-he-fixed-the-wrong-half-and-it-still.md", "text": "https://wpnews.pro/news/a-stranger-fixed-my-bug-then-i-found-out-he-fixed-the-wrong-half-and-it-still.txt", "jsonld": "https://wpnews.pro/news/a-stranger-fixed-my-bug-then-i-found-out-he-fixed-the-wrong-half-and-it-still.jsonld"}}