elastic/elasticsearch · error · IOException
checksum mismatch, expected [{}], actual [{}]
Error message
checksum mismatch, expected [{}], actual [{}] What it means
Thrown by EnterpriseGeoIpDownloader.indexChunks after a Maxmind or Ipinfo database archive is streamed, chunked, and indexed into the .geoip databases index. The bytes received from the provider are hashed (md5, or sha256 when supplied) and the result is compared against the checksum advertised by the provider; a mismatch means the bytes that arrived are not the bytes the provider vouched for. It is an IOException so the downloader task treats it as a transient/retryable download failure rather than a permanent config error.
Source
Thrown at modules/ip-location/src/main/java/org/elasticsearch/ingest/geoip/EnterpriseGeoIpDownloader.java:371
.create(true)
.source(XContentType.SMILE, "name", name, "chunk", chunk, "data", buf);
client.index(indexRequest).actionGet();
chunk++;
}
// May take some time before automatic flush kicks in:
// (otherwise the translog will contain large documents for some time without good reason)
FlushRequest flushRequest = new FlushRequest(DATABASES_INDEX);
client.admin().indices().flush(flushRequest).actionGet();
// Ensure that the chunk documents are visible:
RefreshRequest refreshRequest = new RefreshRequest(DATABASES_INDEX);
client.admin().indices().refresh(refreshRequest).actionGet();
String actualMd5 = MessageDigests.toHexString(md5.digest());
String actualChecksum = digest == null ? actualMd5 : MessageDigests.toHexString(digest.digest());
String expectedChecksum = checksum.checksum;
if (Objects.equals(expectedChecksum, actualChecksum) == false) {
throw new IOException("checksum mismatch, expected [" + expectedChecksum + "], actual [" + actualChecksum + "]");
}
return Tuple.tuple(chunk, actualMd5);
}
// visible for testing
static byte[] getChunk(InputStream is) throws IOException {
byte[] buf = new byte[MAX_CHUNK_SIZE];
int chunkSize = 0;
while (chunkSize < MAX_CHUNK_SIZE) {
int read = is.read(buf, chunkSize, MAX_CHUNK_SIZE - chunkSize);
if (read == -1) {
break;
}
chunkSize += read;
}
if (chunkSize < MAX_CHUNK_SIZE) {
buf = Arrays.copyOf(buf, chunkSize);
}View on GitHub (pinned to db6a809a66)
Solutions
- Wait for the next scheduled download run (the task executor retries on its interval); transient corruption usually self-heals.
- Verify outbound network path to the provider (download.maxmind.com or ipinfo.io) is not intercepted by a captive portal or authenticating proxy returning HTTP 200 with an HTML body.
- Confirm the license key / token is still valid; an auth-failure body can still be 200 bytes that fail the hash.
- Check the cluster logs for the expected vs actual values: a totally wrong actual (e.g. an HTML hash-shaped substring) points to a proxy; a close-but-wrong actual points to truncation.
- If persistent, delete the affected database configuration and re-create it to force a fresh checksum negotiation.
Defensive patterns
Strategy: retry
Try / catch
// the downloader task already catches and retries on its schedule;
// if calling indexChunks directly in a test/tool, wrap it:
try {
downloader.indexChunks(name, in, chunk, checksum, ts);
} catch (IOException e) {
if (e.getMessage().startsWith("checksum mismatch")) {
// discard partial chunks and retry the download from scratch
deleteOldChunks(name, chunk);
throw e; // let the scheduler retry
}
throw e;
} Prevention
- Keep the download path free of intercepting proxies that rewrite bodies.
- Treat checksum mismatch as transient; let the scheduled task retry rather than manual partial fixes.
- Monitor expected vs actual values in logs to distinguish proxy interception from truncation.
- Ensure credentials are valid so auth-failure bodies are not hashed as the database.
When it happens
Trigger: updateDatabases() -> downloaderFor(db).download() -> indexChunks(name, inputStream, chunk, checksum, timestamp). The expected checksum comes from Checksum.sha256(...) (Maxmind) or Checksum.md5(...) (Ipinfo). Mismatch fires at the Objects.equals check on line 370 when the streamed bytes' hash differs from checksum.checksum.
Common situations: Truncated download caused by a dropped connection or proxy timeout; a transparent proxy returning a login/HTML page with HTTP 200; the provider rotating the file mid-download; clock/sync issues causing a stale cached expected checksum; disk or translog write issues corrupting indexed chunks before the hash is computed (less likely since hash is over the source stream).
Related errors
- md5 checksum mismatch, expected [{}], actual [{}]
- Unexpected sha256 response from [{}]
- Unexpected md5 response from [{}]
- too many redirects connection to [{}]
- {} not found
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/bcbdb5b6262cc75c.
Report an issue: GitHub.