Freika/dawarich · warning

[Onboarding] Failed to persist completion:

Error message

[Onboarding] Failed to persist completion:

What it means

The onboarding modal fires a PATCH to onboardingUrlValue to record that the user finished onboarding, guarded only by .catch. fetch only rejects on network-level failures (offline, DNS, aborted request), so HTTP 4xx/5xx statuses pass silently and do NOT produce this message. When it does fire, the analytics event has already been tracked but the server still considers onboarding unfinished, so the modal can reappear next visit.

Source

Thrown at app/javascript/controllers/onboarding_modal_controller.js:199

          screen !== targetName,
        )
      }
    }
  }

  completeOnboarding() {
    this.trackEvent("onboarding_completed")

    if (this.onboardingUrlValue) {
      fetch(this.onboardingUrlValue, {
        method: "PATCH",
        headers: {
          "X-CSRF-Token": document.querySelector('meta[name="csrf-token"]')
            ?.content,
          "Content-Type": "application/json",
        },
      }).catch((error) => {
        console.warn("[Onboarding] Failed to persist completion:", error)
      })
    }
  }

  trackEvent(eventName) {
    if (typeof window.sa_event === "function") {
      window.sa_event(eventName)
    }
  }
}

View on GitHub (pinned to 97fad417c5)

Solutions

  1. Check the Network tab: a PATCH showing a 4xx/5xx status did NOT trigger this catch — handle response.ok separately
  2. Ensure the layout renders csrf_meta_tags so the X-CSRF-Token header is valid
  3. Add an explicit response.ok check and a single retry so transient failures still persist completion
  4. If the modal keeps reappearing, confirm the PATCH reached the server in Rails logs

Example fix

// before
fetch(this.onboardingUrlValue, { method: "PATCH", headers })
  .catch((error) => {
    console.warn("[Onboarding] Failed to persist completion:", error)
  })
// after
try {
  const res = await fetch(this.onboardingUrlValue, { method: "PATCH", headers, keepalive: true })
  if (!res.ok) console.warn("[Onboarding] Persist returned HTTP", res.status)
} catch (error) {
  console.warn("[Onboarding] Failed to persist completion:", error)
  fetch(this.onboardingUrlValue, { method: "PATCH", headers, keepalive: true }).catch(() => {}) // one retry
}
Defensive patterns

Strategy: retry

Validate before calling

const token = document.querySelector('meta[name="csrf-token"]')?.content
if (!this.onboardingUrlValue || !token) {
  console.warn("[Onboarding] Missing URL or CSRF token; skipping persist")
  return
}

Try / catch

try {
  const res = await fetch(this.onboardingUrlValue, { method: "PATCH", headers, keepalive: true })
  if (!res.ok) throw new Error(`HTTP ${res.status}`)
} catch (error) {
  console.warn("[Onboarding] Failed to persist completion:", error)
  fetch(this.onboardingUrlValue, { method: "PATCH", headers, keepalive: true }).catch(() => {})
}

Prevention

When it happens

Trigger: User goes offline or the tab suspends as the modal closes; server or DNS unreachable; the request aborted by Turbo navigation replacing the page; a missing csrf-token meta tag yields a 422 that is a silent failure, not this catch; onboardingUrlValue pointing at a nonexistent route.

Common situations: Mobile Safari suspending the tab on modal dismissal; Rails layout skipping csrf_meta_tags so the X-CSRF-Token header is blank; reverse proxy 502 during deploys; staging URLs left in a copied template.

Related errors


AI-assisted analysis of Freika/dawarich@97fad417c5 (2026-08-21). Data as JSON: /api/errors/6b424d2da3253033. Report an issue: GitHub.