Why is my LinkedIn campaign stuck or showing wrong numbers?
If your LinkedIn campaign is showing zero activity, the dashboard count doesn't match your LinkedIn inbox, or prospects look stuck in a status that never changes, there are a handful of patterns we know about — most of them now have shipped fixes. This page walks through every common 'stuck' shape, what it looks like, why it happens, and what to do.
1) Auto-paused-then-auto-resumed near a sending window boundary (G1 / LL#269, fixed Apr 30, 2026)
Symptom: large cohort of your prospects show status='scheduled' with `next_scheduled_at` set in the future, but no `campaign_send_jobs` row exists for them. Net: nothing actually gets sent. Mark / Selva / Talha all hit this; Selva had 1,097 of 1,208 active InMail prospects orphaned this way on Apr 30 morning.
Why: when a campaign auto-pauses (e.g. on a brief LinkedIn-account disconnect) and then auto-resumes within minutes, the resume can land just outside your sending window. The engine correctly reschedules the prospect's `next_scheduled_at` to the next valid window time, but a separate paired record (the `campaign_send_jobs` row) was left in `processing` with stale fields, so the next sweep flagged it as stuck and the prospect was effectively orphaned. New jobs were never re-enqueued.
Fix: the reschedule path now resets BOTH columns atomically — `campaign_prospects.next_scheduled_at` AND the paired `campaign_send_jobs.run_at` + clears `processing_started_at` + resets `error_count`. A backfill script `backfill-orphaned-window-reschedule-2026-04-30.ts` re-enqueues any prospect that was left orphaned by the pre-fix code path. After the fix lands, your prospects pick back up at the next valid window time and run normally. No action needed on your side.
What 'auto-paused' means in general: it's a deliberate safety pattern (account safety always wins per CLAUDE.md). The engine pauses your campaign on a high-failure-rate signal, a disconnected LinkedIn account, or an upstream Unipile outage to protect the account. Auto-resume re-evaluates the trigger every 5 minutes; once cleared, your campaign picks up where it left off without burning prospects.
2) Account-card daily counter under-reports unified-engine sends (G2 / LL#261, fixed Apr 30)
Symptom: LinkedIn → Accounts shows 'messages sent today: 1' but you can see 24 messages went out in the campaign-card activity log AND in your LinkedIn inbox.
Why: two engines, both correctly atomic for the load-bearing weekly counter (which gates rate limits — never wrong), but the daily-bucket tile under-reported for unified-engine sends. The campaign-card counter and your LinkedIn inbox are always ground-truth.
Fix: every unified-engine action site now writes the paired daily counter in the same transaction as the success event. A reconciler also recomputes daily buckets from event rows on a 30-min cadence so any future drift heals automatically. No action needed on your side — just hard-refresh your dashboard.
3) Reply detection looked broken (G3 / fixed Apr 30)
Symptom: prospect replied on LinkedIn but the dashboard still shows status='invited' or 'connected' — and Slack/n8n/Zapier reply notifications stopped firing.
Why: the canonical `markProspectReplied` helper writes to `campaign_events` but a paired mirror to legacy `linkedin_events` (which Slack and analytics surfaces read) was missing. Inbound messages still landed in the database via the polling fallback, but downstream side-effects (reply counter, stop-on-reply, Slack ping) didn't fire.
Fix: paired mirror added. Backfill `backfill-reply-detection-2026-04-30.ts` re-fits historical NULL-`enrollment_id` rows. Reply stats refresh within ~1 hour after backfill runs. No action needed on your side.
4) Step 2 (send_message) firing on un-accepted prospects (G4 / fixed Apr 30)
Symptom: Mark's Plumbing Campaign — multiple prospects showing 'cannot_message_non_connection' errors even though step 1 was a `condition: linkedin_accepted` check. The condition step is in the campaign but it isn't gating step 2 properly; messages still fire on non-accepted prospects.
Why: the condition evaluator was returning 'advance' when the condition wasn't met yet, instead of 'wait'. So the engine fell through to step 2 even though acceptance hadn't landed.
Fix: the evaluator now returns 'wait' for `linkedin_accepted=false` (and equivalently `linkedin_replied=false`) until the condition is satisfied or the wait window expires (per how YOU configured it — we never auto-shortcut). After the fix, step 2 only fires after the condition is satisfied.
No action needed on your side — your campaign resumes normally.
5) Phantom-accepts (accepted without an invite ever being sent — G5 / fixed Apr 30)
Symptom: prospect shows `linkedin_accepted_at` set + `linkedin_invited_at=NULL`. Selva's 'Reputedd LinkedIn for clients' campaign had 150 prospects in this state.
Why: when a single LinkedIn member is enrolled in 2+ campaigns by the same workspace, the bridge resolver was applying an accept signal across ALL matching enrollments, even ones whose campaign had never actually sent the invite. Cross-campaign signal leak.
Fix: the resolver is now gated on `linkedinInvitedAt IS NOT NULL` so accepts can only be stamped on rows that actually sent the invite. The 150 phantom rows on Reputedd were reverted (we cleared the bogus `linkedin_accepted_at` so the campaign can re-evaluate from scratch). 10 phantom rows on marc@replaiy.ai's accounts also reverted.
No action needed on your side — your dashboard shows the correct shape after the revert.
6) InMail count showing 0 in dashboards (G9 / LL#268, fixed Apr 30)
Symptom: you see InMails landing in your LinkedIn outbox + your campaign-card 'InMails sent' counter incrementing, but a separate 'InMail volume' analytics tile reads 0.
Why: InMail completions never wrote to the legacy `linkedin_events` table because the enum lacked an `inmail_sent` value. Both legacy + unified paths now emit `inmail_sent` correctly. Backfill script seeds the historical 30-day window.
No action needed on your side.
What to do if your dashboard looks wrong:
- Hard-refresh the page (60-second cache).
- Cross-check ground-truth: campaign-card per-step activity counter is always correct; your LinkedIn inbox/sent items is always correct.
- If still wrong after 1 hour, contact support at hello@warmysender.com with your account email and a sample prospect URL — we can reconcile from event rows directly.
What to do if your campaign is auto-paused:
- Check the 'paused reason' on the campaign card. Common reasons: linkedin_account_disconnected, high_failure_rate, user_paused, subscription_canceled.
- If linkedin_account_disconnected: open LinkedIn → Accounts, click Reconnect on the affected row, complete the LinkedIn login. Auto-resume picks up within 5 min.
- If high_failure_rate: review the recent error log for the 30-min window. Most common cause is messages firing before acceptance (G4 above) — verify your step ordering has a `wait_accept` or `condition: linkedin_accepted` BEFORE any `send_message` step.
- If user_paused: click Resume.
- If subscription_canceled: check billing.
Account safety (always wins): every fix above is DB-only — zero new Unipile API calls, no rate-limit risk, no account flags. The engine respects per-account daily/weekly limits, sending windows, and timezone — exactly how you configured them. We never auto-shortcut, never skip steps, never burn prospects to 'help'.
References: PRD-2026-04-30-LINKEDIN-POST-DEPLOY-AUDIT-V4.md, LL#268 + LL#269 in KEY-LESSONS-LINKEDIN.md.