mudler/LocalAI · error · Error

Export failed

Error message

Export failed

What it means

Thrown by exportSkill() when the GET request to the skill export URL (skillsApi.exportUrl(name, userId), credentials same-origin) returns a non-OK status. The message is res.statusText or the literal 'Export failed' when statusText is empty (HTTP/2 responses have no statusText). The fetch downloads a .tar.gz blob and triggers a synthetic anchor click to save it.

Source

Thrown at core/http/react-ui/src/pages/Skills.jsx:97

      danger: true,
      onConfirm: async () => {
        setConfirmDialog(null)
        try {
          await skillsApi.delete(name, userId)
          addToast(t('toasts.deleted', { name }), 'success')
          fetchSkills()
        } catch (err) {
          addToast(err.message || t('toasts.deleteFailed'), 'error')
        }
      },
    })
  }

  const exportSkill = async (name, userId) => {
    try {
      const url = skillsApi.exportUrl(name, userId)
      const res = await fetch(url, { credentials: 'same-origin' })
      if (!res.ok) throw new Error(res.statusText || 'Export failed')
      const blob = await res.blob()
      const a = document.createElement('a')
      a.href = URL.createObjectURL(blob)
      a.download = `${name.replace(/\//g, '-')}.tar.gz`
      document.body.appendChild(a)
      a.click()
      document.body.removeChild(a)
      URL.revokeObjectURL(a.href)
      addToast(t('toasts.exported', { name }), 'success')
    } catch (err) {
      addToast(err.message || t('toasts.exportFailed'), 'error')
    }
  }

  const handleImport = async (e) => {
    const file = e.target.files?.[0]
    if (!file) return
    setImporting(true)

View on GitHub (pinned to 44413a9d06)

Solutions

  1. Re-fetch the skills list and retry export on a fresh entry
  2. If auth is enabled, log in again (same-origin cookie may have expired)
  3. Check the network response status/body in devtools for the real reason
  4. If permissions, fix read access on the skills directory server-side

Example fix

// before
if (!res.ok) throw new Error(res.statusText || 'Export failed')

// after: include the numeric status, which is never empty
if (!res.ok) throw new Error(`Export failed: HTTP ${res.status}${res.statusText ? ` ${res.statusText}` : ''}`)
Defensive patterns

Strategy: retry

Validate before calling

// Confirm the skill still exists before offering/attempting export
async function skillExists(api, name, userId) {
  try {
    const res = await fetch(api.listUrl(userId))
    if (!res.ok) return false
    const { skills = [] } = await res.json()
    return skills.some(s => s.name === name)
  } catch { return false }
}

Try / catch

try {
  const res = await fetch(url, { credentials: 'same-origin' })
  if (!res.ok) throw new Error(`Export failed: HTTP ${res.status}`)
  const blob = await res.blob()
  if (blob.size === 0) throw new Error('Export returned an empty file')
  // ...trigger download
} catch (err) {
  addToast(err.message || t('toasts.exportFailed'), 'error')
}

Prevention

When it happens

Trigger: GET /api/skills/.../export returning 404 (skill renamed/deleted between list and export), 401/403 (session cookie expired — hence credentials: 'same-origin'), or 500 when tarball creation fails server-side.

Common situations: Clicking export on a stale list after the skill was removed; auth cookie expired; skill directory unreadable on the server (permissions); HTTP/2 behind a proxy making statusText empty so every failure shows the generic 'Export failed'.

Related errors


AI-assisted analysis of mudler/LocalAI@44413a9d06 (2026-08-15). Data as JSON: /api/errors/2f4d3826adc07c3a. Report an issue: GitHub.