reticle doctor node v22.14.0 chromium ✓ installed daemon ✓ running on :4400 (pid 24735, v2.7.0) bridge port 4400 (your app must dial THIS port — not your dev-server port) daemon log /Users/you/.reticle/daemon-4400.log tracing RETICLE_TRACE=1 on the daemon for per-stage timings in that log
One command, the whole setup. If this is clean and things still fail, the problem is in the app rather than the wiring.
By far the most common. Reticle’s own diagnostic is unusually good here. It tells you whether the wiring was ever correct:
{ "sessionCount": 0, "why": "no browser session connected — but one WAS connected to this daemon earlier, so the wiring is correct. The tab was closed, navigated away, or hard-reloaded. Ask the human to reopen the app (or run `reticle open`), or reload the tab. You do not have to wait for the human: reticle_lease {action:\"acquire\", url} opens a browser Reticle drives itself."}
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.
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:
[Reticle] could not reach the bridge at ws://localhost:4460/reticle after 3 attempts.Is the Reticle daemon running on that port? If your app runs in a container/devcontainer/WSL,the daemon is on a different host — set the WS URL explicitly(Vite: VITE_RETICLE_WS_URL, or reticle.connect({ url })). Still retrying…
That message names the exact port it tried. Compare it with bridge port from doctor and set the one that is wrong:
VITE_RETICLE_WS_URL=ws://localhost:4400/reticle pnpm dev
The bridge port is not your dev-server port. A dev server on 4312 and a daemon on 4400 is
normal and correct.
Check whether the tab is throttled. Every response tells you:
"warning": "tab throttled; timer/rAF/pointer gestures may silently no-op — refocus before driving"
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:
Drive a lease. A context Reticle owns, never backgrounded:
That ref is stale: refs are invalidated whenever the DOM re-renders, so any action that navigated,opened a modal, re-sorted a list or changed the page invalidates every ref taken before it.Reticle refuses here rather than clicking whatever now occupies that slot.
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.
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:
"feedback_invite": "Reticle could not tell what happened here. If you expected otherwise, reticle_feedback — an unknown verdict is our defect, not yours"
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”.
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.
@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.
npx reticle stop # clean daemon shutdown — no pkill needednpx reticle status # confirm it came back
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.