Crosstalk-Solutions/project-nomad · warning

Failed to dispatch update check

Error message

Failed to dispatch update check

What it means

Thrown by handleCheckUpdates on the supply-depot page when api.checkServiceUpdates() resolves with success !== true (or falsy). The surrounding catch converts it to a toast 'Failed to check for updates: ...'.

Source

Thrown at admin/inertia/pages/supply-depot.tsx:287

    setOpenDropdown(null)
    setLoading(true)
    const result = await api.updateCustomAppImage(service.service_name)
    setLoading(false)
    if (!result?.success) showError(result?.message || 'Failed to update app.')
    else setTimeout(() => window.location.reload(), 1500)
  }

  // Manual trigger for the catalog-wide update check. Results stream back over the
  // SERVICE_UPDATES broadcast (handled by the effect above), which reloads the page.
  async function handleCheckUpdates() {
    if (!isOnline) {
      showError('You must have an internet connection to check for updates.')
      return
    }
    try {
      setCheckingUpdates(true)
      const response = await api.checkServiceUpdates()
      if (!response?.success) throw new Error(response?.message || 'Failed to dispatch update check')
    } catch (error: any) {
      showError(`Failed to check for updates: ${error?.message || 'Unknown error'}`)
      setCheckingUpdates(false)
    }
  }

  // Versioned update for a curated (non-custom) catalog app. Progress + reload are handled
  // by the installActivity effect (update-complete) above.
  async function handleUpdateService(service: ServiceSlim, targetVersion: string) {
    setLoading(true)
    const result = await api.updateService(service.service_name, targetVersion)
    setLoading(false)
    if (!result?.success) showError(result?.message || 'Failed to update service.')
  }

  // Toggle per-app automatic updates (opt-in). Optimistically reflects the new
  // state, reverting if the request fails. Gated by the global master switch in
  // Settings → Updates; this only sets the per-app preference.

View on GitHub (pinned to 0bd1c6f4f9)

Solutions

  1. Look at the toast's embedded message and backend logs for why the check was rejected
  2. Disable the button while checkingUpdates is true to avoid double dispatch
  3. Confirm internet connectivity check passes and the depot endpoint is reachable
  4. Ensure the API wrapper surfaces server-provided message fields

Example fix

// before
if (!response?.success) throw new Error(response?.message || 'Failed to dispatch update check')
// after
if (!response?.success) throw new Error(response?.message || response?.error || 'Failed to dispatch update check')
Defensive patterns

Strategy: try-catch

Validate before calling

if (checkingUpdates) return

Try / catch

try {
  setCheckingUpdates(true)
  const r = await api.checkServiceUpdates()
  if (!r?.success) throw new Error(r?.message || 'Failed to dispatch update check')
} catch (error: any) {
  showError(`Failed to check for updates: ${error?.message || 'Unknown error'}`)
} finally {
  setCheckingUpdates(false)
}

Prevention

When it happens

Trigger: POST/GET to check service updates rejected by the backend: the update-check job is already queued, the depot service is unreachable, or the admin token is invalid so an interceptor returns undefined.

Common situations: Clicking Check Updates while a previous check is still running, appliance without internet (there is a pre-check but it can be wrong), or expired session mid-flight.

Related errors


AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27). Data as JSON: /api/errors/c7baa903be203ef8. Report an issue: GitHub.