agalwood/Motrix · error · AppError

GeoIPDatabaseInvalid

GeoIPDatabaseInvalid

Error message

database too small (${buffer.length} bytes)

What it means

Thrown after the full download is buffered: if Buffer.concat(chunks).length is below MIN_VALID_SIZE_BYTES, the file is too small to be a real GeoIP database and is rejected as GeoIPDatabaseInvalid. This catches truncated/error-page downloads that nevertheless returned HTTP 200.

Source

Thrown at src/core/geoip/geo-ip-downloader.ts:128

      }

      const reader = body.getReader()
      while (true) {
        const { value, done } = await reader.read()
        if (done) break
        if (value) {
          chunks.push(value)
          bytesReceived += value.byteLength
          emit(false)
        }
      }
    } finally {
      clearTimeout(timeoutId)
    }

    const buffer = Buffer.concat(chunks)
    if (buffer.length < MIN_VALID_SIZE_BYTES) {
      throw new AppError(
        ErrorCode.GeoIPDatabaseInvalid,
        `database too small (${buffer.length} bytes)`
      )
    }
    if (!hasMmdbSentinel(buffer)) {
      throw new AppError(
        ErrorCode.GeoIPDatabaseInvalid,
        'MaxMind.com metadata marker not found — file is not a valid .mmdb'
      )
    }

    try {
      // writeFileAtomic handles fsync + rename + tmp cleanup on
      // failure, so we no longer need an explicit unlink in the
      // catch path.
      await writeFileAtomic(dbPath, buffer)
    } catch (err) {
      throw new AppError(

View on GitHub (pinned to 1a708ee577)

Solutions

  1. Open the downloaded URL in a browser to confirm it actually serves the full database.
  2. Check content-length against MIN_VALID_SIZE_BYTES in the response headers before trusting the body.
  3. Retry — partial delivery is often transient.
  4. Point the source URL at the canonical full database archive.
  5. Inspect network/proxy MTU or compression settings that may truncate the body.

Example fix

// before
const buffer = Buffer.concat(chunks)
if (buffer.length < MIN_VALID_SIZE_BYTES) {
  throw new AppError(ErrorCode.GeoIPDatabaseInvalid, `database too small (${buffer.length} bytes)`)
}

// after — reject early when the server itself reports an undersized body
if (bytesTotal > 0 && bytesTotal < MIN_VALID_SIZE_BYTES) {
  throw new AppError(ErrorCode.GeoIPDatabaseInvalid, `server reports only ${bytesTotal} bytes`)
}
const buffer = Buffer.concat(chunks)
if (buffer.length < MIN_VALID_SIZE_BYTES) {
  throw new AppError(ErrorCode.GeoIPDatabaseInvalid, `database too small (${buffer.length} bytes)`)
}
Defensive patterns

Strategy: validation

Validate before calling

// Use the server's content-length to reject undersized payloads early.
const total = Number.parseInt(response.headers.get('content-length') ?? '', 10)
if (Number.isFinite(total) && total > 0 && total < MIN_VALID_SIZE_BYTES) {
  throw new AppError(ErrorCode.GeoIPDatabaseInvalid, `server reports only ${total} bytes; aborting download`)
}

Try / catch

try {
  await downloader.download(url)
} catch (err) {
  if (err instanceof AppError && err.code === ErrorCode.GeoIPDatabaseInvalid && /too small/.test(err.message)) {
    // re-fetch from a known-good mirror or report bad source URL
  } else throw err
}

Prevention

When it happens

Trigger: buffer.length < MIN_VALID_SIZE_BYTES. The server returned a small payload (truncated file, a short HTML error page with 200, a redirect-stub, or a partial read due to early disconnect).

Common situations: Upstream CDN served a stub/error page with status 200; connection dropped mid-download but a tiny body was captured; the configured URL points to a metadata file rather than the database; an aggressive proxy truncated the response.

Related errors


AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12). Data as JSON: /api/errors/8bdef5bd6e68fb97. Report an issue: GitHub.