Skip to main content
Most of these were hit while writing these docs. The diagnostics quoted are real output.

Start here

One command, the whole setup. If this is clean and things still fail, the problem is in the app rather than the wiring.

”No browser session connected”

By far the most common. Reticle’s own diagnostic is unusually good here. It tells you whether the wiring was ever correct:
Read the why. “One WAS connected earlier” means stop debugging your config, just reopen the tab. If nothing ever connected:
  • Is the app running, and open in a browser?
  • Is reticle.connect() actually executing? Check the dev guard is true.
  • Port mismatch. This is the big one.

The port mismatch

Your app dials a port; the daemon listens on one. When they disagree, the app runs perfectly and silently never connects. The browser console says so:
That message names the exact port it tried. Compare it with bridge port from doctor and set the one that is wrong:
The bridge port is not your dev-server port. A dev server on 4312 and a daemon on 4400 is normal and correct.

The click did nothing

Check whether the tab is throttled. Every response tells you:
A backgrounded browser tab has its timers suppressed. Synthetic pointer gestures and timers can silently no-op, so an action reports success and nothing happens. Three fixes, in order of preference:
  1. Drive a lease. A context Reticle owns, never backgrounded:
    Release it when you finish: { "action": "release", "sessionId": "lease-…" }.
  2. Focus the tab.
  3. Fail loudly instead of pretending. Pass refuseWhenThrottled: true to your action.
If the tab is fine, check occluded on the element. A click that lands on a modal overlay is the other half of “the button doesn’t work”:

“ref no longer resolves to an element”

This is Reticle protecting you, not failing. Re-query for a fresh ref. Better: use reticle_act_and_wait { until } for actions that change the page, so the next ref is taken after things settle.
Refs never survive a full navigation. A new document means new refs, the same button was e5 before a navigation and e103 after.

The verdict says unknown

verified: "unknown" means Reticle drove the app and could not tell. It is not a pass. The usual cause is a throttled tab: throttling suppresses the quiescence detection used to decide the page has settled, so even a successful action can come back unsettled. Drive a lease and try again. If it persists on an unthrottled context, that is worth reporting. Reticle’s own response says so:

A negative result you are not sure about

Before concluding “no request fired”, read the buffer note:
“Nothing found” and “I no longer have what you asked about” are different answers. Grade sooner, or widen the buffer.

The agent cannot find an element

  • reticle_snapshot { mode: "interactive" } shows everything actionable.
  • Add a data-testid for a stable handle.
  • Narrow with scope. A CSS selector or a ref.

Assertions are flaky on async UIs

  • Use timeout_ms on assert and wait_for.
  • Pass the since cursor from reticle_act so only post-action events count. Without it, an assertion can pass on an event from two clicks ago.

reticle_state returns nothing

storeNames empty means no store was registered. reticle_state reads what your app registered in src/reticle-dev.ts.
Pass the store, not () => store.getState(). The getter form is read-only and silently produces empty stateDiffs. Which reads as “nothing changed” and means “I was never watching”.

Source file is not resolving on React 19

React 19 dropped _debugSource. Wire up @reticlehq/babel-plugin. Or just use @reticlehq/vite-plugin or @reticlehq/next, which both include it. Without it you get component identity but no file:line.

ERESOLVE installing next to a prerelease

@reticlehq/next declares peer next >=13 and @reticlehq/react declares peer react >=18. On a Next canary or React RC, npm refuses. See the prerelease notes.

Restarting things

Do not kill port 4400 with lsof -ti tcp:4400 | xargs kill -9. That matches the MCP proxy as well as the daemon and takes your agent’s connection down with it. Use reticle stop, or at minimum lsof -sTCP:LISTEN.

Sessions piling up

Stale sessions pointing at dev servers you restarted an hour ago look completely plausible in a list, and one of them will get picked.
reticle_session { action: "end" } when you are done, { action: "yield" } when pausing. Both revive on your next action.

Still stuck?

reticle feedback --agent works even when nothing is installed and the daemon will not start.
Last modified on August 14, 2026