apache/kafka · error · GradleException

Failed to check internal API usage: {errorMessage}

Error message

Failed to check internal API usage: {errorMessage}

What it means

Wrapped I/O failure raised by KafkaInternalApiCheckerTask.runCheck() when PublicApiChecker.checkBytecode(classRoots) throws an IOException while reading the kafka dependency jars or scanning the compiled class roots. The original IOException is attached as the cause and its message is appended to the GradleException, so the underlying reason (unreadable jar, missing class file, closed stream) is preserved for diagnosis. This is an infrastructure/I-O error, not an API-surface violation.

Source

Thrown at api-checker/gradle-plugins/src/main/java/org/apache/kafka/gradle/KafkaInternalApiCheckerTask.java:120

    private void handleNoKafkaDependency() {
        String msg = "No org.apache.kafka:* dependencies found on the configured "
                + "kafkaDependencyJars. The checker cannot derive an API surface and would "
                + "produce a meaningless '0 violations' report — likely a classpath or "
                + "configuration issue.";
        if (failOnNoKafkaDependency.get()) {
            throw new GradleException(msg);
        }
        getLogger().warn("{} Skipping internal API check. "
                + "Set kafkaInternalApiChecker.failOnNoKafkaDependency = true to make this fatal.", msg);
    }

    private void runCheck(List<File> kafkaJars, List<File> classRoots) {
        try {
            getLogger().info("Scanning {} class file root(s) for internal API usage", classRoots.size());
            CheckResult result = new PublicApiChecker(kafkaJars).checkBytecode(classRoots);
            reportResults(result);
        } catch (IOException e) {
            throw new GradleException("Failed to check internal API usage: " + e.getMessage(), e);
        }
    }

    private void reportResults(CheckResult result) throws IOException {
        List<PublicApiViolation> violations = result.violations();
        List<PublicApiViolation> suppressions = result.suppressions();

        ViolationReporter reporter = new ViolationReporter();
        File report = reportFile.get().getAsFile();
        reporter.writeTextReport(violations, suppressions, report);
        reporter.printToConsole(violations, suppressions);

        getLogger().info("Internal API usage check completed. Report written to: {}", report.getAbsolutePath());

        long unjustified = suppressions.stream().filter(PublicApiViolation::lacksReason).count();
        if (unjustified > 0) {
            getLogger().warn("{} suppression(s) carry no reason — KIP-1265 requires a justification on every @SuppressKafkaInternalApiUsage", unjustified);
        }

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Re-run with --rerun-tasks after a clean to rule out a stale/partial build dir: ./gradlew clean checkInternalApiUsage --rerun-tasks.
  2. Validate the Kafka dependency jars are not corrupt: re-resolve dependencies with --refresh-dependencies (Gradle) and check ~/.gradle/caches for partial files.
  3. Inspect the IOException cause in the stack trace for the exact path that failed, then verify that path is readable (ls -l, file <jar>) and fix permissions / free disk / re-download.
  4. If running under a Gradle daemon, stop daemons (./gradlew --stop) to rule out a daemon holding a stale file handle, then re-run.
  5. On shared/network filesystems, ensure the build dir and Gradle cache live on a local volume.
Defensive patterns

Strategy: validation

Validate before calling

from release.notes import query, filter_unresolved

def unresolved_issues_for_version(version):
    """Return the list of issues tagged with version that are not fully resolved."""
    issues = query(f"project=KAFKA and fixVersion={version}")
    return filter_unresolved(issues)

# Caller-side guard before invoking generate(version):
unresolved = unresolved_issues_for_version(version)
if unresolved:
    report = "\n".join(f"{i.key} -> {i.fields.resolution}" for i in unresolved)
    raise SystemExit(f"Cannot generate notes for {version}; resolve or untag these first:\n{report}")
html = generate(version)

Try / catch

try:
    html = generate(version)
except Exception as e:
    msg = str(e)
    if "is not complete since there are unresolved" in msg:
        # Parse the embedded issue list from the message body and surface it
        # structurally rather than as a raw blob.
        sys.exit(f"Release {version} blocked by unresolved issues:\n{msg}")
    raise

Prevention

When it happens

Trigger: Lines 117-120 in runCheck(): a new PublicApiChecker(kafkaJars) or checkBytecode(classRoots) call throws IOException. Concrete triggers: a kafka dependency jar on the classpath is corrupt, truncated, or concurrently deleted; a configured classDirs entry (default build/classes) was removed mid-build; a network-mounted filesystem returned a read error; permissions deny read on a .class file under build/classes.

Common situations: Concurrent Gradle daemons / other tools mutating build/classes or the Gradle cache while the check runs; an interrupted dependency download leaving a partial jar in ~/.gradle/caches; CI on a container with a stale or read-only mount of the build dir; antivirus / SELinux denying file reads; disk-full conditions truncating jar extraction.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/94de562f3d0b1851.json. Report an issue: GitHub.