TEASEDocs
ProductsLinkTraffic, clicks & redirect

Troubleshooting

Common symptoms in the click/redirect pipeline, their real cause, and where to look in code.

CTR/visits look inflated, extra unique visits every day

Symptom: Daily visit counts include traffic that doesn't match any real campaign or channel — a steady background trickle that inflates every domain roughly the same amount.

Cause: Synthetic self-traffic — local health-checks and watchers — hitting the funnel from a loopback subnet, which used to count as a real visit.

Check: the click buffer rejects any subnet starting with 127. before it's ever recorded. If this regresses, the guard lives in record() (backend/app/products/link/core/click/log.py:156-166).

Non-owner tenants' clicks never age out under retention

Symptom: A multi-page/multi-tenant account's clicks table keeps growing past the retention window for every tenant except the main one.

Cause: The retention prune used to scope its delete to the empty-string owner only, so any tenant with a real owner_id was silently skipped every day.

Check: prune() must delete on a clean Click.ts < cutoff with execution_options(skip_tenant=True) — no owner filter at all — so the cutoff reaches every tenant's rows (backend/app/products/link/core/click/log.py:391-411).

/r/paid or /r/ftl suddenly leads to a different offer than before

Symptom: A smart-link address that used to always mean "the paid offer" (or "the trial offer") now resolves to something else.

Cause: These addresses are aliases, not a fixed inventory slot — they're handed off to the domain's own smart-link policy (Route Spine) like any other slug, so they follow whatever that policy currently serves rather than a frozen offer.

Check: a hit on either legacy address logs a WARNING-level line naming the host and what it was actually served, so you can see who's still pointing at the old address (backend/app/api/of_redirect.py:1094-1132).

One visit shows up as two different click ids in the click report

Symptom: The same visitor's journey through a button and then a downstream /r redirect produces two separate rows with two different click ids instead of one coherent journey.

Cause: Without carry-through, the button click (/api/go) and the smart-link redirect it hands off to (/r/...) each mint their own click id independently.

Check: confirm the outbound URL to /r carries ?cid=<the button's click id> (via outbound_cid.append_r_carry), and that /r accepts that incoming cid instead of always minting a fresh one (_sanitize_cid, backend/app/api/of_redirect.py:230-246).

A moderator bot's clicks show up in the audit but never move the funnel/fraud counters

Symptom: A known moderation/scanner bot's clicks are visible in the raw click log but don't appear to move any conversion, funnel, or fraud total.

Cause: By design — every cloaked bot click is logged (is_bot = 1) for auditability, but it's excluded from every count that matters via one shared predicate.

Check: any aggregation over clicks must filter with human_clicks() (backend/app/core/click/filters.py:22-29). If a bot's clicks ARE moving a count somewhere, look for a query that's missing .where(human_clicks()).

What's next

Back to Traffic, clicks & redirect for the tasks these symptoms show up in.

On this page