Troubleshooting
Nothing is recording at all
Symptom: Sessions never appear in the dashboard.
Likely causes and fixes:
-
Recorder script not injected. Check page source for
<script id="sentiero-config">. If it’s missing, the layout helper isn’t running. Confirmsentiero_script_tag(Rails/Roda) orSentiero::Web::ScriptTag.renderis called in the layout that’s actually rendered for the page you’re testing. -
Wrong
events_url. The config JSON (<script id="sentiero-config">) contains"eventsUrl". Check that it matches the path whereEventsAppis mounted. Mismatch silently drops events; the browser POSTs to the wrong URL and gets a 404. - Events endpoint not mounted. Verify the events route is mounted and reachable. A quick test:
curl -X POST http://localhost:PORT/sentiero/events \ -H "Content-Type: application/json" \ -d '{"sessionId":"test","windowId":"test","events":[{"type":4,"timestamp":1}]}'A mounted, working endpoint returns
200 {"status":"ok"}. A missing route returns404. - Store not configured.
Sentiero.storeraisesSentiero::Errorifconfig.storeisniland no default was set. The Rails engine auto-setsSentiero::Rails::Store.new; other frameworks require an explicitconfig.store =inSentiero.configure. Check your boot logs for an uncaught error on the first event POST.
Events POST succeeds but dashboard shows nothing
Symptom: The events endpoint returns 200 but sessions never appear.
-
Route mount order. In plain Rack
mapblocks, the dashboard mount (map "/sentiero") must not shadow the events mount (map "/sentiero/events"). If both are present, order doesn’t matter withmap, but verify there’s no catch-all intercepting requests first. -
Store write silently failing. Enable error logging and watch server output when an event batch is sent. If
save_eventsraises, the batch is dropped.
CORS errors in the browser console
Symptom: Browser console shows CORS policy errors on POST /sentiero/events.
The events endpoint enforces CORS using config.cors_origins. If the origin of your page is not in the list, the browser blocks the request.
Fix:
Sentiero.configure do |config|
config.cors_origins = ["https://yoursite.com"]
# For local dev:
# config.cors_origins = ["http://localhost:3000"]
end
cors_origins = [] (the default) means no cross-origin requests are permitted. If the recorder and your app are on the same origin, CORS is not needed.
GPC or opt-out silently suppressing recording
Symptom: Recording works for some users but not others, with no errors.
Two privacy mechanisms can suppress recording without any visible error:
-
Global Privacy Control (
respect_gpc).config.respect_gpcdefaults totrue. When the browser exposesnavigator.globalPrivacyControl === true, the recorder does not start and no POST is sent to your server. This is intentional and correct. -
End-user opt-out. If
config.user_opt_out = true, users who have calledwindow.Sentiero.optOut()(or who have the opt-out cookie set) are not recorded. The cookie name defaults to"sentiero_optout". Check browser cookies/localStorage for that key.
To rule these out in dev, temporarily set config.respect_gpc = false and clear the opt-out cookie.
Dashboard returns 403 or a blank page
Symptom: /sentiero returns 403.
-
auth_callbackis returning falsy. Check that the callback receives the expected session/env state. The callback is called with the raw Rackenvhash; inspectenv["rack.session"]or whatever your auth mechanism populates. Exceptions in the callback are caught and treated as 403 (fail-closed). -
Route-level auth is rejecting the request before it reaches
DashboardApp.
Events are buffered but the dashboard lags
Symptom: Recording works, but sessions take a while to appear or show fewer events than expected.
For faster feedback in development, lower the flush settings (see Configuration for defaults):
Sentiero.configure do |config|
config.flush_interval_ms = 2_000
config.flush_event_threshold = 10
end
Don’t set these too low in production; each flush is an HTTP request.
Recorder JS 404
Symptom: The <script src="..."> for the recorder returns 404.
-
The assets endpoint is not reachable. There is no Rails engine asset pipeline for this. In Rails the recorder JS is served by
DashboardAppitself, from its/assets/*route, which runs before the auth check (so assets stay public even when the dashboard is protected). In Roda/Rack, mount the standaloneSentiero::Web::AssetsAppexplicitly at/sentiero/assetsand callr.sentiero_assets(Roda) orrun Sentiero::Web::AssetsApp.newinside the assetsmapblock (Rack). -
Route-level auth is shadowing the assets path (Rails). If you wrapped the
DashboardAppmount in a Deviseauthenticate/route constraint, that constraint also gates/sentiero/assets/*, so anonymous visitors get a 401/redirect onrecorder.js. Either switch toconfig.auth_callback/config.basic_auth(assets short-circuit before the auth check) or mountSentiero::Web::AssetsApppublicly outside the constraint. See Authentication. -
The recorder URL is being inferred from
events_url. The asset filename is content-hashed (e.g.recorder-TBGP22CQ.js), so there is no stable un-hashedrecorder.jsto link directly. If you need to override it, pass the assets mount base asrecorder_url:and let the manifest resolve the digested filename, for examplesentiero_script_tag(events_url: "/my/events", recorder_url: "/my/assets"). The default inference handles this automatically whenevents_urlfollows the standard/…/eventspattern.