Skip to content

fix(browser): surface mqtt-js errors during CONNECT as CONNECTION_FAILURE#698

Open
kamal wants to merge 2 commits into
awslabs:mainfrom
kamal:fix/browser-mqtt5-surface-connect-errors
Open

fix(browser): surface mqtt-js errors during CONNECT as CONNECTION_FAILURE#698
kamal wants to merge 2 commits into
awslabs:mainfrom
kamal:fix/browser-mqtt5-surface-connect-errors

Conversation

@kamal
Copy link
Copy Markdown

@kamal kamal commented Mar 3, 2026

Summary

When the browser MQTT5 client encounters an error from mqtt-js during the Connecting lifecycle state, the error is silently swallowed:

  1. on_browser_client_error stores the error in lastError and emits it on the INFO event
  2. No CONNECTION_FAILURE event is ever emitted
  3. The underlying WebSocket can remain open indefinitely with no MQTT frames sent
  4. The connection hangs silently — consumers and the reconnection scheduler never learn about the failure

Reproduction

One concrete way to trigger this: pass a Uint8Array as the password to newWebsocketMqttBuilderWithCustomAuth. The CRT types accept binary data, but the underlying mqtt-js v4 writeToStream rejects anything that isn't string | Buffer with Error: Invalid password. In the browser there is no native Buffer, so Uint8Array instanceof Buffer is always false.

The WebSocket handshake succeeds (HTTP 101), but mqtt-js fails to serialize the CONNECT packet and emits an error event. The CRT wrapper catches this in on_browser_client_error, stores it in lastError, and emits on INFO — but since no MQTT frames are sent, the server never closes the WebSocket, on_browser_close is never called, and CONNECTION_FAILURE is never emitted.

Fix

When an error occurs while lifecycleEventState == Connecting, force-close the mqtt-js client so that the normal on_browser_closeCONNECTION_FAILURE path fires. This lets consumers and the reconnection scheduler react to the failure.

Test plan

  • Verify existing browser MQTT5 tests still pass
  • Confirm that passing an invalid password type triggers CONNECTION_FAILURE instead of hanging
  • Confirm the reconnection scheduler picks up the failure and retries

🤖 Generated with Claude Code

kamal and others added 2 commits March 3, 2026 11:52
…LURE

When the browser MQTT5 client encounters an error from mqtt-js during the
Connecting lifecycle state (e.g. mqtt-js rejecting a Uint8Array password
with "Invalid password"), the error is stored in lastError and emitted on
the INFO event, but no CONNECTION_FAILURE is ever emitted. The underlying
WebSocket can remain open indefinitely with no MQTT frames sent, causing
the connection to hang silently.

Force-close the mqtt-js client when an error occurs during Connecting so
that the on_browser_close path fires and emits CONNECTION_FAILURE as
expected. This allows consumers and the reconnection scheduler to react
to the failure instead of hanging forever.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a test that verifies connectionFailure is emitted when mqtt-js
rejects the password during CONNECT packet serialisation (e.g. when
a Uint8Array is passed instead of string|Buffer in the browser).

Without the fix in on_browser_client_error, this test would hang
forever because the error was only emitted on the INFO event and the
WebSocket would never close.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kamal kamal closed this Mar 3, 2026
@kamal kamal reopened this Mar 3, 2026
@bretambrose
Copy link
Copy Markdown
Contributor

This seems like a reasonable workaround (tbh I think mqtt-js should handle this case on their own as well). However, we are very near moving off of mqtt-js entirely (probably a couple of weeks) so I'm unsure if it's worth pushing this through. If you feel strongly about this I can push it through in the short-term though.

@kamal
Copy link
Copy Markdown
Author

kamal commented Mar 3, 2026

@bretambrose I don't feel strongly about it. Interested to know what you'll be moving to?

@bretambrose
Copy link
Copy Markdown
Contributor

@bretambrose I don't feel strongly about it. Interested to know what you'll be moving to?

An internal implementation that follows the same architecture and approach as the native mqtt5 client. PRs are up; what remains is an integration PR that switches everything over and some stress testing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants