A stranger fixed my bug – but fixed the wrong half and it still worked

A pull request fixed a Safari MCP bug, but half the patch was a no-op. The real issue? A missing invariant repeated across seven functions. Learn the lesson.

miércoles, 29 de julio de 2026 • 5 min read • Q2BSTUDIO Team

La lección oculta en una corrección de código que parecía perfecta

Receiving an external contribution always sparks some anticipation. When a developer from outside your project submits a pull request fixing a bug you had not detected yourself, the first reaction is usually gratitude. But when you sit down to analyze the code line by line, you discover that half of the patch does not change the behavior at all, while the other half —the one the author barely mentioned— is what actually resolves the failure. That was the experience of the developer responsible for a Safari MCP server, and it serves as a masterclass on how the lack of invariant documentation can turn the most innocent code into a silent ticking bomb.

The technical scenario is a simple communication protocol between a Node.js process and a native Swift helper. Requests and responses are correlated by FIFO order, without unique identifiers. A queue of callbacks waits for responses, and each incoming response is assigned to the first callback in the queue. This design works perfectly as long as every sent request has a pending response. But if a request is abandoned without actually being sent —for example, due to a write error on the pipe— the queue becomes permanently desynchronized. The next call's response goes to the zombie callback, and the real requester waits until the timeout fires. The symptom is never an error, but a hang that can go unnoticed for hours.

The contributor, with good judgment, identified that those orphaned callbacks needed to be handled. He submitted a patch that modified three identical functions by replacing the abandoned callback with an explicit empty consumer. That seemed sensible, but on analyzing the existing code it turned out that the original callback already protected itself with a boolean flag (resolved), so the replacement was functionally identical. The real fix was in the catch block of the write, where the PR author not only cleared the timeout but also removed the entry from the queue with a splice. That line, almost hidden in the diff, was what prevented the write error from poisoning all subsequent calls. The rest of the patch was a comment that compiles.

What is fascinating about this case is not just that the contributor got the correct part right despite misdescribing the solution, but that the original code author himself had written the same pattern seven times in the same file. In four of those seven functions he included a defensive line —an empty consumer for late responses— but without documenting why it was necessary. In the other three, that line simply was not there, though the write bug existed in all of them. The absence of a centralized function encapsulating the invariant forced the developer to remember an unwritten rule every time a new helper call was added. The result was a roulette: four out of seven times the developer remembered to apply the cosmetic defense, but the real failure —the handling of the write error— was never addressed in any of the seven.

At Q2BSTUDIO, a company specialized in custom software development, we know that duplicated code is not just an aesthetic issue. Every copy is an opportunity for the next developer —including yourself months later— to mentally reconstruct the business rules or system invariants. When those rules are not documented, the risk of introducing subtle errors multiplies. And when the system handles shared states that transcend a single call, as happens in many automation processes or AI agent systems, a synchronization failure can have catastrophic consequences without anyone raising an alarm.

The lesson goes beyond this specific case. The ID-less protocol is just an example of how simple designs can hide deep complexities. In the cybersecurity domain, for instance, a desynchronization error in authentication can lead to unauthorized access. In cloud AWS/Azure environments, poorly managed message queues can cause data loss. Even in BI/Power BI tools, incorrect query correlation can generate reports with wrong data. The root cause is always the same: an unwritten invariant that everyone assumes but nobody has formalized.

The real fix, both in the MCP server case and in any software project, is not to apply a one-off patch but to refactor so that the rule becomes explicit in a single place. A function _helperRequest that centralizes the timeout, write error, and queue cleanup logic removes the dependency on human memory. The contributor fixed three instances, but the technical debt remained in the file's structure. The original author took responsibility for that refactoring, acknowledging that the real bug was not in the missing lines but in the absence of a place where those lines should be.

This case also reminds us that automated tests do not always cover the most relevant failure scenarios. The 54 tests in the project all passed, but none simulated a broken pipe because that test is not written until the bug has already happened. At Q2BSTUDIO, when developing turnkey software solutions, we insist on including integration tests that exercise error paths and shared states across calls. Only then can we reduce the gap between what the code should do and what it actually does under adverse conditions.

In the end, the anecdote of the pull request that fixed the wrong half leaves us with a valuable reflection: in software development, especially when working with distributed architectures, queues, autonomous agents, or real-time systems, documenting invariants is not optional. It is as important as the code itself. And if we manage to encapsulate that logic in a single function or module, we will be protecting all future maintainers —including ourselves— from repeating the same mistake over and over. Because, as this story shows, human memory fails; well-designed code should not.

A BREAK?

Play for a moment before you go

OUR SERVICES

How we can help you

Do you have a project in mind?

Tell us your vision and we'll turn it into a software solution. Whatever the scope, we make your idea real.