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

  1. Check server logs for the '[ZimController] Upload failed:' line — the real underlying error is logged there
  2. 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
  3. Ensure the tmp directory the controller writes to exists and is writable (volume mounted, correct permissions in Docker)
  4. 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

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


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