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
- Re-fetch the skills list and retry export on a fresh entry
- If auth is enabled, log in again (same-origin cookie may have expired)
- Check the network response status/body in devtools for the real reason
- 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
- Include the numeric HTTP status in the thrown message — statusText is empty on HTTP/2
- Send credentials explicitly (as this code does) and refresh the list before exporting stale entries
- Verify the downloaded blob is non-empty and has a gzip/tar signature before declaring success
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
- HTTP ${r.status}
- status: HTTP ${statusRes.status}
- HTTP ${response.status}
- HTTP ${res.status}
- HTTP ${response.status}
AI-assisted analysis of mudler/LocalAI@44413a9d06 (2026-08-15).
Data as JSON: /api/errors/2f4d3826adc07c3a.
Report an issue: GitHub.