languagetool-org/languagetool · error · IOException

Checksum mismatch for the file

Error message

Checksum mismatch for the file '${path}': expected ${requiredHash}, got ${computedHash}

What it means

Tools.getStream reads a file fully, computes its SHA-256 hash, and compares it against a caller-supplied required hash before returning the data as a stream. An IOException with this message is thrown when the content has changed or the expected hash is wrong, guaranteeing callers never process tampered or stale data.

Solutions

  1. Recompute the expected SHA-256 hash from the current file content (shasum -a 256 <path>) and pass that as requiredHash
  2. Re-download or restore the original file from the trusted source that matches requiredHash
  3. If the file legitimately changed, update the pinned hash in the calling code/config
  4. Check for proxy or transfer corruption by re-fetching the file and comparing hashes

Example fix

// before
InputStream in = Tools.getStream(path, "abc123oldhash");
// after
String hash = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(Path.of(path))));
InputStream in = Tools.getStream(path, hash);
Defensive patterns

Strategy: validation

Validate before calling

String actual = HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(Files.readAllBytes(Path.of(path))));
if (!actual.equals(requiredHash)) throw new IOException("Checksum mismatch: " + path);
InputStream in = Tools.getStream(path, requiredHash);

Try / catch

try (InputStream in = Tools.getStream(path, requiredHash)) { ... } catch (IOException e) { if (e.getMessage().startsWith("Checksum mismatch")) { reDownloadFile(path); } else { throw e; } }

Prevention

When it happens

Trigger: Calling Tools.getStream(path, requiredHash) where the file bytes changed after the expected hash was recorded, the hash was computed on a different file/version, or the hash string itself is malformed/outdated.

Common situations: Downloading cached language-model or resource files whose upstream content was updated; reusing a pinned hash across releases; manual edits to a data file after hashing.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.


AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06). Data as JSON: /api/errors/34d436b4646eb3a9. Report an issue: GitHub.

Appendix: source

Thrown at languagetool-core/src/main/java/org/languagetool/tools/Tools.java:285

    InputStream is = JLanguageTool.getDataBroker().getAsStream(path);
    if (is == null) {
      throw new IOException("Could not load file from classpath: '" + path + "'");
    }
    return is;
  }

  public static InputStream getStream(String path, String requiredHash) throws IOException, NoSuchAlgorithmException {
    byte[] data;

    try (InputStream is = getStream(path)) {
      data = is.readAllBytes();
    }

    MessageDigest md = MessageDigest.getInstance("SHA-256");
    String computedHash = HexFormat.of().formatHex(md.digest(data));

    if (!computedHash.equals(requiredHash)) {
      throw new IOException("Checksum mismatch for the file '" + path + "': expected " + requiredHash + ", got " + computedHash);
    }
    return new ByteArrayInputStream(data);
  }

  /**
   * Enable and disable rules of the given LanguageTool instance.
   * @param lt LanguageTool object
   * @param disabledRuleIds ids of the rules to be disabled
   * @param enabledRuleIds ids of the rules to be enabled
   * @param useEnabledOnly if set to {@code true}, disable all rules except those enabled explicitly
   */
  public static void selectRules(JLanguageTool lt, List<String> disabledRuleIds, List<String> enabledRuleIds, boolean useEnabledOnly) {
    Set<String> disabledRuleIdsSet = new HashSet<>();
    disabledRuleIdsSet.addAll(disabledRuleIds);
    Set<String> enabledRuleIdsSet = new HashSet<>();
    enabledRuleIdsSet.addAll(enabledRuleIds);
    selectRules(lt, Collections.emptySet(), Collections.emptySet(), disabledRuleIdsSet, enabledRuleIdsSet, useEnabledOnly, false);
  }

View on GitHub (pinned to 2e990059ce)