fbpx logo-new mail facebook Dribble Social Icon Linkedin Social Icon Twitter Social Icon Github Social Icon Instagram Social Icon Arrow_element diagonal-decor rectangle-decor search arrow circle-flat
Development

Solved: The Mystery of the Nefarious Hanging Socket and How To Debug With Better Logging

Toby Leonard Engineering

While running a new test for a fix in an existing app, I ran into an unexpected error with an equally unexpected solution.

The app was Node.js-based using the HTTP library to serve up a REST API, and the test standard jest fare. I was trying to ensure one of the API endpoints handled a particular error gracefully. The test code looked something like this:

Pretty straightforward, but jest was failing with socket hang up — no stack trace or other error. And I hadn’t even changed the actual code under test yet!

Since this was 1) async and 2) TypeScript, I figured I had something off in my mock setup, and spun my wheels trying to suss that out. From searching online, it sounded like the mock wasn’t returning a Promise so the test framework was timing out waiting for an actual response. As you might guess by how I elided the mock setup above, that was not the case.

I had a coworker look at it with me, and the first thing they suggested was turning on the logging — always a great first step, but I had been console.log()‘ing so far instead. Once we did so, it was easy to see that the server was sending back an HTTP response, including the status code I expected (123), but the client was still sitting there waiting. And curiously, the response didn’t include the JSON error payload it should’ve had.

That was the “aha!” moment. I remembered that 1XX-level HTTP responses are informational. They’re hints from the server or other commands (like switching protocols to WebSockets), but they aren’t the actual response. Therefore the client can take them under advisement but is still waiting for a proper reply. Since the testing I was doing involved actual HTTP server and client code, they were both behaving as expected. My throwaway value of 123 for the status code meant the client running via Jest was left hanging.

Here are my takeaways from this:

  1. Turn on logging while debugging/testing. The old joke is “There’s a rumor that printf() is useful, but this is probably unfounded.”
  2. Bring in someone else once you’re reasonably sure you’ve exhausted what you know to do. I spent too long by myself on this one, figuring the issue was some arcane combo of TypeScript and Jest syntax. Some good advice I’ve heard around this is to time-box yourself once you feel like you’re hitting a wall; give it another 20-30 min, then ask around.
  3. Keep your data in mind when testing. In this case, the culprit was a somewhat obscure HTTP behavior. But something like unexpected Unicode data, case- or accent-sensitivity on a DB column, or similar scenarios could make seemingly innocuous data like Fabrício Testcase fail oddly.
Let’s do something great together

We do our best work in close collaboration with our clients. Let’s find some time for you to chat with a member of our team.

Say Hi