Skip to main content

The device picker never opens

connect()/start() must run inside a user gesture (a click handler); browsers block device prompts otherwise.

The device picker opens every time

It should not: after the first pairing, connect() reconnects through the browser’s stored grant with no prompt. If the picker keeps appearing, check these, in order.
  • Are you calling requestDevice() on every connect? It always opens the chooser, by design. Use it only for pairing - see Connecting once, not every time.
  • Grants are per top-level origin. localhost:3000, a preview URL, and your production domain are separate stores, so operators pair once per environment.
  • Private/incognito windows never persist a grant.
  • Some USB-serial readers report no serial number. Chrome can only store a grant permanently for a device it can identify stably; for the rest the grant lives in memory and is dropped when the reader is unplugged or the browser restarts. Refresh and in-page navigation are still prompt-free.
  • A kiosk that must never prompt needs the Chrome enterprise policy SerialAllowUsbDevicesForUrls. That is an IT deployment step, not something a page can do.

The reader is held by another tab or application

Serial devices are exclusive. If a second tab, an encoder utility, or a terminal session already holds the reader, opening it fails and the connection state becomes error rather than authorization-required - re-pairing will not help. Close the other holder, or call disconnect() in the tab that no longer needs it.

Phone NFC permission is per-origin

Moving your operator flow to a new domain prompts every operator again, and a denied permission never re-prompts on its own - it must be re-enabled from the address-bar lock icon → Permissions → NFC. Screen overlays from other apps also block the prompt. The SDK surfaces both states through onError with operator-actionable instructions.

”Serial blocklist” lines in the browser console

When the device picker opens, Chrome logs one line per nearby Bluetooth device it excludes (for example, wireless headphones). That output comes from the browser itself - it is expected and harmless.

Still stuck?

The package ships an AGENTS.md for AI coding agents, and these docs are agent-readable - see Use Endstate with AI tools for the fastest ways to debug an integration with your tooling.