janhq/jan · error · Error

Backend update failed

Error message

Backend update failed

What it means

The catch-all backend-update failure: `updateBackend` returned `wasUpdated: false` with no `reason` at all, or a shape that matched none of the handled branches (not `true`, not `false`+`in_progress`, not `false`+explicit-reason). It indicates the extension returned an unexpected or empty failure response.

Source

Thrown at web-app/src/hooks/useBackendUpdater.ts:391

        result?.wasUpdated === false &&
        (result.reason === 'in_progress' || typeof result.reason === 'undefined')
      ) {
        // Benign no-op (e.g., another update is already in progress or the
        // extension returned a no-op response without a reason). Do not treat
        // this as a failure; just clear the local isUpdating flag.
        setUpdateState((prev) => ({
          ...prev,
          isUpdating: false,
        }))
      } else if (
        result?.wasUpdated === false &&
        result.reason &&
        result.reason !== 'in_progress'
      ) {
        // Explicit failure reason from extension: surface as an error.
        throw new Error(`Backend update failed: ${result.reason}`)
      } else {
        throw new Error('Backend update failed')
      }
    } catch (error) {
      console.error('Error updating backend:', error)
      setUpdateState((prev) => ({
        ...prev,
        isUpdating: false,
      }))
      throw error
    }
  }, [updateState.updateInfo, updateState.isUpdating, syncStateToOtherInstances])

  const installBackend = useCallback(async (filePath: string) => {
    try {
      // Get llamacpp extension instance
      const allExtensions = ExtensionManager.getInstance().listExtensions()
      const llamacppExtension =
        ExtensionManager.getInstance().getByName('llamacpp-extension')

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Update the llamacpp extension so its `updateBackend` returns the documented `{ wasUpdated, reason? }` shape.
  2. Log the raw `rawResult` before branching to capture the unexpected shape.
  3. Treat a missing `wasUpdated` as a soft failure and poll `checkBackendForUpdates` to confirm actual state.

Example fix

// before
} else {
  throw new Error('Backend update failed')
}

// after
} else {
  console.error('updateBackend returned unexpected shape:', rawResult)
  throw new Error('Backend update failed: the extension returned an unexpected response.')
}
Defensive patterns

Strategy: try-catch

Type guard

function isWellFormedUpdateResult(r: unknown): r is { wasUpdated: boolean; reason?: string } {
  return !!r && typeof (r as any).wasUpdated === 'boolean'
}

Try / catch

const rawResult = await extension.updateBackend?.(targetBackendString)
if (!isWellFormedUpdateResult(rawResult)) {
  console.error('Unexpected updateBackend shape:', rawResult)
  // Fall back to re-checking actual state
  const info = await extension.checkBackendForUpdates?.()
  toast[info?.updateNeeded ? 'error' : 'success'](
    info?.updateNeeded ? 'Backend update status unclear' : 'Backend is up to date'
  )
  return
}

Prevention

When it happens

Trigger: The extension resolved with an undefined/null result, an object missing `wasUpdated`, or `{ wasUpdated: false }` with no reason. Typically an extension bug, an older API version returning a different shape, or an unhandled internal path.

Common situations: Extension version that returns `{ ok: true }` instead of `{ wasUpdated: true }`; a thrown-but-swallowed error inside the extension that resolved to a bare rejection caught upstream; API contract drift between the hook and the extension.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/2e19955eb186e7bb. Report an issue: GitHub.