The workflow
I run bb on a Linux VPS and reach it from a Mac through bb Connect (https://<name>.getbb.app). The workflow is: open the desktop app in the morning and keep working in the threads that ran overnight.
- Save a Connect server as the desktop target (Window ▸ Server ▸ pick the Connect server).
- Put the Mac to sleep, or get on a network that cannot reach the saved server for a moment.
- Open the bb desktop app.
- The server becomes reachable a second later — Wi-Fi finished associating, the VPN settled, the relay reconnected.
Step 4 is the point: by the time I read the error screen, the server is already reachable again, and the app has no way to act on that.
What happens today
The app shows the startup error screen and stays there:
Could not reach this bb server
The bb server at https://<name>.getbb.app did not answer. Check that the machine is awake and reachable, then choose Window ▸ Server to retry this server or switch to This Mac. Logs are under /Users/<me>/.bb/logs/.
The screen is a dead end. There is no control on it, so recovery means going up to the menu bar, opening Window ▸ Server and re-picking the server that is already selected. That is the only supported way back, and nothing on the screen says the server that failed is still the one that is selected.
loadRemoteServerPage makes exactly one loadUrl attempt and renders the error on any rejection — no retry, no backoff, no re-check:
|
export async function loadRemoteServerPage( |
|
args: LoadRemoteServerPageArgs, |
|
): Promise<boolean> { |
|
try { |
|
await args.loadUrl({ url: args.serverUrl }); |
|
return true; |
|
} catch (error) { |
|
if (!args.isCurrent()) { |
|
return false; |
|
} |
|
const label = describeServerUrl(args.serverUrl); |
|
args.logWarning( |
|
`[desktop] could not load ${label}: ${formatLoadFailure(error)}`, |
|
); |
|
await args.loadStartupError({ |
|
details: |
|
`${label.charAt(0).toUpperCase()}${label.slice(1)} did not answer. ` + |
|
"Check that the machine is awake and reachable, then choose " + |
|
"Window ▸ Server to retry this server or switch to " + |
|
`${BUILTIN_SERVER_NAME}.`, |
|
logs: "", |
|
title: "Could not reach this bb server", |
|
}); |
|
return false; |
|
} |
|
} |
This is the follow-up to #1494 and #1524. Those replaced the raw Electron stack with a readable screen, which shipped in 0.40.0 and is a clear improvement — but the screen still cannot recover on its own.
What I ruled out. On my side the server was healthy the whole time. The Connect tunnel had not dropped for 2.5 hours before the failure (plugins/connect/logs/plugin.log shows the last reconnect at 20:06:59, the failure at ~22:46), the relay answered a request from that same Mac a minute later with status=200 totalMs=26, and a phone on a different network opened the same URL immediately. So this is not a server outage: it is a one-shot load attempt losing a race it is never given a chance to re-run.
Versions and environment
- bb desktop 0.42.1 (macOS, Apple Silicon), server 0.42.0 on Linux (Ubuntu, Node 24), reached over bb Connect.
- Reproduced against
main at 06aeaa9.
What you would expect
A button on the error screen — one click, no menu trip. Concretely: a Try again button that re-applies the currently selected server target, shown only for failures a retry can actually fix (server unreachable, Connect auth failed, local runtime failed to start) and hidden for the ones it cannot (port conflict, "could not stop the running bb", a fatal startup error).
I put a prototype on a fork, in case the shape is useful: https://github.com/xMinor-1/bb/tree/desktop-retry-button-on-startup-error
local-view.ts renders the button when the view model says the failure is retryable; the flag is explicit at every loadStartupError call site, so no screen gets a button by accident.
- The click goes over one validated IPC channel and calls the existing
applyServerTarget(), so Connect re-auth and builtin-runtime attach are covered by the same path the Window ▸ Server menu already uses. A loading view is shown while it reconnects.
pnpm exec turbo run typecheck --filter=@bb/desktop and test --filter=@bb/desktop pass (300 tests, 41 files), oxfmt --check clean. New tests cover the rendered button, the IPC handler rejecting payloads a renderer should never send, and the preload wiring.
- One existing test needed a change:
preload-browser-api.test.ts imports preload.ts with no window/document in scope, so the new listener needs the globals stubbed there.
Happy to adjust the shape, the label, or narrow which screens get the button — or to drop the prototype entirely if you would rather solve it with an automatic retry.
Context and alternatives
The workaround today is the menu trip, every time. For a laptop that sleeps between sessions this happens often enough to be the main friction with the desktop app.
Two smaller things I noticed while digging, happy to split either into its own issue if you want them tracked:
- The screen says "Logs are under ~/.bb/logs/", but the desktop process logs through
createDesktopLogger, which writes to process.stderr (
|
function createDesktopLogger(): DesktopAutoUpdateLogger { |
|
return { |
|
error(message) { |
|
process.stderr.write(`${message}\n`); |
|
}, |
|
info(message) { |
|
process.stderr.write(`${message}\n`); |
|
}, |
|
warn(message) { |
|
process.stderr.write(`${message}\n`); |
|
}, |
|
}; |
|
} |
). Launched from Finder that goes nowhere, and ~/.bb/logs/ holds only server logs — so the [desktop] could not load …: ERR_* line that names the actual failure cannot be found by following the screen's own advice. I had to reproduce from a terminal to see it.
- The Connect tunnel has no keepalive ping (
plugins/connect/src/tunnel.ts has no ping/pong; it only reconnects on close), and closes with code 1006 between 7 and 19 times a day on my host. It recovers in about a second each time, so it is minor on its own — but every one of those seconds is a window where a desktop cold start hits the dead-end screen.
Checks
AGENT GENERATED
The workflow
I run bb on a Linux VPS and reach it from a Mac through bb Connect (
https://<name>.getbb.app). The workflow is: open the desktop app in the morning and keep working in the threads that ran overnight.Step 4 is the point: by the time I read the error screen, the server is already reachable again, and the app has no way to act on that.
What happens today
The app shows the startup error screen and stays there:
The screen is a dead end. There is no control on it, so recovery means going up to the menu bar, opening Window ▸ Server and re-picking the server that is already selected. That is the only supported way back, and nothing on the screen says the server that failed is still the one that is selected.
loadRemoteServerPagemakes exactly oneloadUrlattempt and renders the error on any rejection — no retry, no backoff, no re-check:bb/apps/desktop/src/remote-server-load.ts
Lines 34 to 59 in 06aeaa9
This is the follow-up to #1494 and #1524. Those replaced the raw Electron stack with a readable screen, which shipped in 0.40.0 and is a clear improvement — but the screen still cannot recover on its own.
What I ruled out. On my side the server was healthy the whole time. The Connect tunnel had not dropped for 2.5 hours before the failure (
plugins/connect/logs/plugin.logshows the last reconnect at 20:06:59, the failure at ~22:46), the relay answered a request from that same Mac a minute later withstatus=200 totalMs=26, and a phone on a different network opened the same URL immediately. So this is not a server outage: it is a one-shot load attempt losing a race it is never given a chance to re-run.Versions and environment
mainat 06aeaa9.What you would expect
A button on the error screen — one click, no menu trip. Concretely: a Try again button that re-applies the currently selected server target, shown only for failures a retry can actually fix (server unreachable, Connect auth failed, local runtime failed to start) and hidden for the ones it cannot (port conflict, "could not stop the running bb", a fatal startup error).
I put a prototype on a fork, in case the shape is useful: https://github.com/xMinor-1/bb/tree/desktop-retry-button-on-startup-error
local-view.tsrenders the button when the view model says the failure is retryable; the flag is explicit at everyloadStartupErrorcall site, so no screen gets a button by accident.applyServerTarget(), so Connect re-auth and builtin-runtime attach are covered by the same path the Window ▸ Server menu already uses. A loading view is shown while it reconnects.pnpm exec turbo run typecheck --filter=@bb/desktopandtest --filter=@bb/desktoppass (300 tests, 41 files),oxfmt --checkclean. New tests cover the rendered button, the IPC handler rejecting payloads a renderer should never send, and the preload wiring.preload-browser-api.test.tsimportspreload.tswith nowindow/documentin scope, so the new listener needs the globals stubbed there.Happy to adjust the shape, the label, or narrow which screens get the button — or to drop the prototype entirely if you would rather solve it with an automatic retry.
Context and alternatives
The workaround today is the menu trip, every time. For a laptop that sleeps between sessions this happens often enough to be the main friction with the desktop app.
Two smaller things I noticed while digging, happy to split either into its own issue if you want them tracked:
createDesktopLogger, which writes toprocess.stderr(bb/apps/desktop/src/main.ts
Lines 543 to 555 in 06aeaa9
~/.bb/logs/holds only server logs — so the[desktop] could not load …: ERR_*line that names the actual failure cannot be found by following the screen's own advice. I had to reproduce from a terminal to see it.plugins/connect/src/tunnel.tshas no ping/pong; it only reconnects on close), and closes with code 1006 between 7 and 19 times a day on my host. It recovers in about a second each time, so it is minor on its own — but every one of those seconds is a window where a desktop cold start hits the dead-end screen.Checks