grpc/grpc-java · error · GeneralSecurityException

Files were unmodified before their initial update. Probably…

Error message

Files were unmodified before their initial update. Probably a bug.

What it means

This one-shot updateTrustCredentials(File) call returns the file's new modification time from readAndUpdate; a return value of 0 means the file's lastModified() did not change from the initial value (0), so no credentials were read. The library treats this as an internal invariant violation — the very first read should always register a modification time.

Solutions

  1. Verify the trust certificate file exists and is readable, and that its filesystem reports a valid modification time (File.lastModified() > 0) before calling
  2. Re-read the file / retry the update call once the file is stable on disk
  3. If the file is actively rotated, ensure the rotation is atomic (write temp file + rename) so lastModified() and the read are consistent
  4. Report as a bug if the file is a regular local file with a valid mtime — the library comment says 'Probably a bug'

Example fix

// before
manager.updateTrustCredentials(trustCertFile); // throws if mtime is 0
// after
if (trustCertFile.exists() && trustCertFile.lastModified() > 0) {
  manager.updateTrustCredentials(trustCertFile);
} else {
  throw new IOException("Trust cert file missing or has invalid mtime: " + trustCertFile);
}
Defensive patterns

Strategy: validation

Validate before calling

if (trustCertFile == null || !trustCertFile.exists() || trustCertFile.lastModified() == 0) {
  throw new IOException("Trust cert file missing or unreportable mtime: " + trustCertFile);
}
trustManager.updateTrustCredentials(trustCertFile);

Try / catch

try {
  manager.updateTrustCredentials(trustCertFile);
} catch (GeneralSecurityException e) {
  if (e.getMessage().contains("Files were unmodified before their initial update")) {
    log.warning("Initial trust update failed; verify file mtime/filesystem: " + trustCertFile);
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling public void updateTrustCredentials(File trustCertFile) when readAndUpdate(trustCertFile, 0) returns 0. Since oldTime is 0, this only happens when File.lastModified() returns 0, i.e. the file does not exist or is not readable — normally that path throws the IOException from readAndUpdate first, so reaching 0 here indicates a race or filesystem anomaly (lastModified()==0 but FileInputStream succeeded, e.g. on odd filesystems).

Common situations: Files on exotic/network filesystems where lastModified() returns 0; a file deleted or replaced between the lastModified() check and the read; TOCTOU races in rapidly rotating certificate files.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of grpc/grpc-java@64daddc1f3 (2026-09-08). Data as JSON: /api/errors/71b8e113ff54cc71. Report an issue: GitHub.

Appendix: source

Thrown at util/src/main/java/io/grpc/util/AdvancedTlsX509TrustManager.java:231

    int i = 1;
    for (X509Certificate cert: trustCerts) {
      String alias = Integer.toString(i);
      keyStore.setCertificateEntry(alias, cert);
      i++;
    }
    this.delegateManager = createDelegateTrustManager(keyStore);
  }

  /**
   * Updates the trust certificates from a local file path.
   *
   * @param trustCertFile  the file on disk holding the trust certificates
   */
  public void updateTrustCredentials(File trustCertFile) throws IOException,
      GeneralSecurityException {
    long updatedTime = readAndUpdate(trustCertFile, 0);
    if (updatedTime == 0) {
      throw new GeneralSecurityException(
          "Files were unmodified before their initial update. Probably a bug.");
    }
  }

  /**
   * Schedules a {@code ScheduledExecutorService} to read trust certificates from a local file path
   * periodically, and updates the cached trust certs if there is an update. You must close the
   * returned Closeable before calling this method again or other update methods
   * ({@link AdvancedTlsX509TrustManager#useSystemDefaultTrustCerts()},
   * {@link AdvancedTlsX509TrustManager#updateTrustCredentials(X509Certificate[])},
   * {@link AdvancedTlsX509TrustManager#updateTrustCredentialsFromFile(File)}).
   * Before scheduling the task, the method synchronously reads and updates trust certificates once.
   * If the provided period is less than 1 minute, it is automatically adjusted to 1 minute.
   *
   * @param trustCertFile  the file on disk holding the trust certificates
   * @param period the period between successive read-and-update executions
   * @param unit the time unit of the initialDelay and period parameters
   * @param executor the executor service we use to read and update the credentials

View on GitHub (pinned to 64daddc1f3)