History, errors, and enhancement

Handle connection errors

Distinguish a server response from a request that never completed and provide a retry path.

A connection error is different from an HTTP error. No usable server response arrived at all. HTMX emits htmx:sendError. Timeouts and aborted requests emit their own events.

Provide one shared recovery path:

document.addEventListener('htmx:sendError', () => {
  const status = document.querySelector('#connection-status')
  if (status) status.textContent = 'Connection lost. Check your network and retry.'
})

HTMX removes request-state classes and restores elements managed by hx-disabled-elt as the lifecycle ends. Preserve safe form input, replace indefinite “Saving…” text, and leave a clear retry control.

Picture this on the task form. You submit “Buy milk”, the connection drops, and DevTools shows (failed) net::ERR_INTERNET_DISCONNECTED with no response body. The title field should still contain “Buy milk”. The submit button should be enabled again. The message should say you could not confirm the result, not “Save failed.”

For a state-changing request, “no response” does not prove “nothing happened.” The server may have created the task before the connection failed. Blindly clicking Add again can create “Buy milk” twice. Use idempotency keys or fetch /tasks before retrying so the user sees what actually saved.

You can listen for htmx:timeout when you set a request timeout, and htmx:abortError when hx-sync or navigation cancels an in-flight request. Aborted requests are not failures. Someone typed a new search term, or navigated away. Do not show “Connection lost” for those.

A practical pattern is one #connection-status region updated only for genuine send errors, plus per-form error markup returned from the server for HTTP failures. That split keeps messages accurate.

Test offline mode, timeout, abort, and a real 500 separately in DevTools. They should not all collapse into the same diagnosis. Connection problems deserve different copy and recovery steps than server errors.

For a 500, the server did respond. You should see status 500 in the Network panel and htmx:responseError in the console. For offline, the row shows (failed) and no status code. Treating both as “connection lost” sends users down the wrong recovery path.

Lesson completed