Crosstalk-Solutions/project-nomad · error
Upload failed
Error message
Upload failed
What it means
Generic 500 returned by the ZimController upload endpoint whenever the multi-part ZIM file upload fails for any reason (disk write error, missing file part, size limit, service-layer failure). The controller cleans up its temp file, logs the real cause via logger.error, but deliberately hides it behind 'Upload failed'.
Source
Thrown at admin/app/controllers/zim_controller.ts:168
}
if (!filename) {
return response.status(400).send({ message: 'No file received' })
}
const { added } = await this.zimService.registerLocalUpload(filename)
return response.status(201).send({
message: 'ZIM file uploaded and registered successfully',
filename,
added,
})
} catch (error) {
logger.error('[ZimController] Upload failed:', error)
if (tmpPath) {
const { unlink } = await import('fs/promises')
await unlink(tmpPath).catch(() => {})
}
return response.status(500).send({ message: 'Upload failed' })
}
}
// Wikipedia selector endpoints
async getWikipediaState({}: HttpContext) {
return this.zimService.getWikipediaState()
}
async selectWikipedia({ request }: HttpContext) {
const payload = await request.validateUsing(selectWikipediaValidator)
return this.zimService.selectWikipedia(payload.optionId)
}
// Custom library endpoints
async listCustomLibraries({}: HttpContext) {
return this.zimService.listCustomLibraries()View on GitHub (pinned to 0bd1c6f4f9)
Solutions
- Check server logs for the '[ZimController] Upload failed:' line — the real underlying error is logged there
- Verify the adonisjs bodyparser config allows large files (request multipart file size limits) and that the client sends the file under the expected field name
- Ensure the tmp directory the controller writes to exists and is writable (volume mounted, correct permissions in Docker)
- Retry with a smaller/known-good ZIM file to isolate payload corruption from environment issues
Example fix
// config/bodyparser.ts — allow large ZIM uploads
// before
fileTypes: {},
// after
file: {
maxFileSize: '8gb',
}, Defensive patterns
Strategy: try-catch
Validate before calling
// before upload: check file exists client-side
if (!file || file.size === 0) throw new Error('Empty file')
if (!file.name.endsWith('.zim')) throw new Error('Expected a .zim file') Type guard
const isFilePart = (p: unknown): p is { file: File; name: string } =>
!!p && typeof p === 'object' && p.file instanceof File && typeof p.name === 'string' Try / catch
try {
const res = await fetch('/admin/zim/upload', { method: 'POST', body: formData })
if (!res.ok) throw new Error(`Upload failed (${res.status})`)
} catch (e) {
// surface to UI; check server logs for '[ZimController] Upload failed:' detail
notifyUser('Upload failed — see server logs for details')
} Prevention
- Check server logs first — the 500 hides the real cause
- Pre-validate file size against bodyparser limits before uploading
- Stream large ZIM files instead of buffering in tmp when possible
When it happens
Trigger: POST to the ZIM upload route with a malformed/missing file part, a file exceeding body size limits, a full/readonly tmp directory, or when zimService throws during processing (e.g. invalid ZIM file).
Common situations: adonis bodyparser maxFileSize too small for multi-GB ZIM files; tmp dir not writable in container; client sending wrong field name for the file; corrupted or truncated ZIM upload over flaky connection.
Related errors
- Failed to fetch remote ZIM files.
- Maximum of 10 custom libraries allowed
- Custom library not found
- ${error.message}
- Could not fetch directory listing from the provided URL
AI-assisted analysis of Crosstalk-Solutions/project-nomad@0bd1c6f4f9 (2026-08-27).
Data as JSON: /api/errors/685103a33424e540.
Report an issue: GitHub.