grpc/grpc-java · error · IOException

Certificate file not found or not readable

Error message

Certificate file not found or not readable: ${trustCertFile.getAbsolutePath()}

What it means

readAndUpdate calls File.lastModified() on the trust certificate file; a return value of 0 means the file does not exist or cannot be read (or the filesystem cannot report its mtime). The library surfaces this as an IOException naming the offending path so the caller knows which certificate file is unavailable.

Solutions

  1. Verify the file exists at the exact absolute path (File.exists() / canRead()) before configuring the trust manager
  2. Use an absolute path or confirm the process working directory matches the relative path assumption
  3. Check filesystem permissions for the user running the process (and mount the cert file in containers)
  4. If the file is rotated, do so atomically (temp file + rename) and ensure it always exists
  5. If the file exists but lastModified() is still 0, move it to a local filesystem that reports mtimes

Example fix

// before
File cert = new File("trust.pem"); // resolved against unknown cwd
manager.updateTrustCredentials(cert);
// after
File cert = new File("/etc/app/tls/trust.pem");
if (!cert.exists() || !cert.canRead()) {
  throw new FileNotFoundException("Trust cert missing/unreadable: " + cert.getAbsolutePath());
}
manager.updateTrustCredentials(cert);
Defensive patterns

Strategy: validation

Validate before calling

File cert = new File(trustCertPath);
if (!cert.isFile() || !cert.canRead() || cert.lastModified() == 0) {
  throw new FileNotFoundException("Trust cert not found/readable: " + cert.getAbsolutePath());
}

Try / catch

try {
  manager.updateTrustCredentials(certFile);
} catch (IOException e) {
  if (e.getMessage().startsWith("Certificate file not found or not readable")) {
    throw new ConfigurationException("Fix trust cert path/permissions: " + e.getMessage(), e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Any call path through readAndUpdate — one-shot updateTrustCredentials(File), scheduled updateTrustCredentials(File, long, TimeUnit, ScheduledExecutorService), or the periodic refresh task — when trustCertFile.lastModified() returns 0 because the path is missing, unreadable, or on a filesystem that does not expose modification times.

Common situations: Typo'd or relative path resolved against the wrong working directory; certificate file deleted before first use; container images built without the cert file mounted; permissions tightened after deployment; files on network mounts returning mtime 0.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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

Appendix: source

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

        log.log(Level.SEVERE, String.format("Failed refreshing trust CAs from file. Using "
            + "previous CAs (file lastModified = %s)", file.lastModified()), e);
      }
    }
  }

  /**
   * Reads the trust certificates specified in the path location, and updates the key store if the
   * modified time has changed since last read.
   *
   * @param trustCertFile  the file on disk holding the trust certificates
   * @param oldTime the time when the trust file is modified during last execution
   * @return oldTime if failed or the modified time is not changed, otherwise the new modified time
   */
  private long readAndUpdate(File trustCertFile, long oldTime)
      throws IOException, GeneralSecurityException {
    long newTime = checkNotNull(trustCertFile, "trustCertFile").lastModified();
    if (newTime == 0) {
      throw new IOException(
          "Certificate file not found or not readable: " + trustCertFile.getAbsolutePath());
    }
    if (newTime == oldTime) {
      return oldTime;
    }
    FileInputStream inputStream = new FileInputStream(trustCertFile);
    try {
      X509Certificate[] certificates = CertificateUtils.getX509Certificates(inputStream);
      updateTrustCredentials(certificates);
      return newTime;
    } finally {
      inputStream.close();
    }
  }

  // Mainly used to avoid throwing IO Exceptions in java.io.Closeable.
  public interface Closeable extends java.io.Closeable {
    @Override

View on GitHub (pinned to 64daddc1f3)