← ALL PATCH NOTES

Console chat actually sends

v0.8.2 Fix
  • Live chat on the console now wires up on the new app shell. The Send button looked dead because the page was never subscribed to the broadcast. It is now.
  • Messages render optimistically the instant you hit Send, and the logged-in-but-asked-to-log-in flash is gone.
THE BUILD LOG

The live chat was never live, and the fix made it worse for ten minutes

Three commits in fifteen minutes. The third one reverts the second, and explains why the second made the whole page hang.

There is a live chat in this product. On 2026-05-18 it was moved out of a floating widget in the corner and onto its own full page, taking over a slot in the navigation. Better home for it, more room, easier to use.

It stopped working immediately, and nobody noticed for ten days.

The symptom, when it was finally reported, is a good one. You type a message, you press send, and nothing happens. Not an error. Not a spinner. The message simply does not appear.

What was actually happening is worse than nothing happening. The message was being sent. It reached the server, the server accepted it, the server broadcast it to everyone connected. The only person who did not receive it was the person who sent it, because their page had never subscribed to the channel.

One gate, wrong side

The chat's initialisation was guarded by a condition that meant, roughly, "only do this on pages that are not using the new application shell." That was correct when the chat lived in a floating widget on old pages.

Then the chat moved to its own page, and that page uses the new shell. So the one page in the entire product containing the chat interface was the one page where the chat code was told not to run.

The send button still worked, because the send button is wired separately. It called the send function, which tried the real-time connection, found none, and quietly fell back to a plain HTTP request. That request succeeded. Everything downstream of it succeeded. The failure was entirely in the receiving half, and the sending half had a fallback good enough to hide it.

That is a specific and nasty class of bug: a feature that half works, where the half that works is the half that reports success. If the send had failed too, this would have been found in ten seconds.

The fix at 00:12 was small. The commit body ends with "Reported by Tyler," which in this project means somebody used their own product and it did not behave.

Ten minutes of a worse product

At 00:17, five minutes later, a follow-up. There was a brief flash of a login prompt while the page worked out whether you were signed in, so the fix was to wait for the session check before starting the chat. Obvious improvement. Seven lines.

At 00:27, ten minutes after that, a commit that begins by reverting it.

The reason is worth quoting in substance: on a cold start, the endpoint that answers "who is this user" can take thirty seconds or more, because the server has to wake up first. Waiting for it before initialising did not just delay the chat. It blocked the entire startup chain behind it. Form wiring, the experience bar, every piece of page-specific setup, all queued behind one slow request. What had been a brief cosmetic flash became the page appearing to hang on every refresh.

Five minutes to introduce it, ten minutes to find it, and the loop only closed that fast because the person who wrote it was also the person refreshing the page.

The lesson is one I keep relearning. Adding an await in front of something is the smallest possible edit and one of the largest possible behavioural changes, because you have just made everything after it depend on the slowest thing you know about. A cosmetic flicker is a cosmetic problem. Serialising your startup behind a network call that can take thirty seconds is an architectural one. Trading the first for the second is a bad trade made by an edit that looks like nothing.

The third commit also stopped pretending in two other places.

It admitted that the real-time layer is not real-time. The socket library was configured in a mode that cannot actually complete a WebSocket upgrade, so every client silently falls back to long polling, and messages broadcast between them arrive unreliably. Fixing that properly is infrastructure work, and it was not going to happen at half past midnight. So the chat renders your own message locally the instant you send it, which makes it feel live without depending on a broadcast that may not come back. That is a workaround and the commit says so.

And it replaced an empty catch block with a real error log. There had been a catch that swallowed connection failures and did nothing, which is why a broken real-time connection produced no evidence anywhere. Every time you write a silent catch you are making a bet that the code inside it cannot fail in a way you would want to know about, and you are usually wrong.

There is a coda. That long-polling fallback, the one this commit worked around rather than fixed, turned out to be considerably more expensive than anyone realised, in a way that only shows up under a server model where held-open requests are a finite resource. It was chased through three separate fixes and a pile of configuration before anyone understood the shape of it.

It was finally root-caused and removed in August, and the fix was to delete the real-time layer entirely and make the chat a plain HTTP poll on a three second cursor. Less clever, and it stopped being able to hurt anything else.

Three commits, fifteen minutes, one revert, and a workaround that lasted until somebody understood what it was hiding.

— DealArena Team