{"id":"28d7d8bce87fab85","repo":"apache/kafka","slug":"found-d-public-api-violations-see-report-s","errorCode":null,"errorMessage":"Found %d public API violations. See report: %s","messagePattern":"Found (.+?) public API violations\\. See report: (.+?)","errorType":"exception","errorClass":"GradleException","httpStatus":null,"severity":"error","filePath":"api-checker/gradle-plugins/src/main/java/org/apache/kafka/gradle/KafkaPublicApiCheckerTask.java","lineNumber":122,"sourceCode":"\n            // Print summary to console\n            reporter.printToConsole(violations, suppressions);\n\n            getLogger().info(\"Public API check completed. Report written to: {}\", report.getAbsolutePath());\n\n            if (!suppressions.isEmpty()) {\n                getLogger().lifecycle(\"{} suppression(s) honoured — see report for justifications.\", suppressions.size());\n                long unjustified = suppressions.stream().filter(PublicApiViolation::lacksReason).count();\n                if (unjustified > 0) {\n                    getLogger().warn(\"{} suppression(s) carry no reason — KIP-1265 requires a justification on every @SuppressKafkaInternalApiUsage\", unjustified);\n                }\n            }\n            if (!violations.isEmpty()) {\n                String message = String.format(\"Found %d public API violations. See report: %s\",\n                    violations.size(), report.getAbsolutePath());\n\n                if (failOnViolation.get()) {\n                    throw new GradleException(message);\n                } else {\n                    getLogger().warn(message);\n                }\n            } else {\n                getLogger().info(\"No public API violations found.\");\n            }\n\n        } catch (IOException e) {\n            throw new GradleException(\"Failed to check public API: \" + e.getMessage(), e);\n        }\n    }\n\n    private File getJavadocJarFile() {\n        if (!javadocJarPath.isPresent()) {\n            throw new GradleException(\"kafkaPublicApiChecker.javadocJarPath is not set. \"\n                    + \"Either configure it explicitly on the extension, or apply this plugin to a \"\n                    + \"project that defines a 'javadocJar' Jar task whose output the plugin can \"\n                    + \"wire automatically.\");","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/api-checker/gradle-plugins/src/main/java/org/apache/kafka/gradle/KafkaPublicApiCheckerTask.java#L104-L140","documentation":"Thrown by KafkaPublicApiCheckerTask.checkPublicApi() when checkPublicApiConsistency(jarFile) returned one or more violations and failOnViolation is true (its default). A violation here means a mismatch between the public API surface declared by @InterfaceAudience.Public annotations in the project jars and the published javadoc — e.g. a type annotated @InterfaceAudience.Public is missing from the javadoc jar, or the javadoc documents a type that is not part of the annotated public surface.","triggerScenarios":"Line 121-122: result.violations() is non-empty and failOnViolation.get() is true. Produced when a class is annotated @InterfaceAudience.Public but the javadoc jar lacks the corresponding HTML page (or vice versa), or when the @InterfaceAudience annotation was added/removed without regenerating the javadoc.","commonSituations":"Promoting an internal class to public by adding @InterfaceAudience.Public but forgetting to regenerate javadoc; removing the annotation from a previously-public class whose javadoc page still exists; an upstream javadoc generation that silently skipped a class due to an error; misaligned javadoc jar version vs the source jars used for the annotation scan.","solutions":["Open the report at the path in the message and read each violation to identify whether the javadoc is stale (page missing) or the annotation is wrong.","If the annotation was added/changed, regenerate the javadoc jar (./gradlew javadocJar --rerun-tasks) so HTML and annotations agree.","If the annotation is wrong, correct it: add @InterfaceAudience.Public to a class that should be public, or remove it from one that should not.","If a violation is intentional and justified, suppress it with @SuppressKafkaInternalApiUsage(reason = \"...\") per KIP-1265.","During migration only, set kafkaPublicApiChecker.failOnViolation = false to surface the report without failing while triage happens."],"exampleFix":"// before\n@InterfaceAudience.Public\npublic class NewClient { ... }\n// javadocJar was not regenerated -> violation: NewClient missing from javadoc\n\n// after — regenerate the docs so the published surface matches\n// ./gradlew clean javadocJar kafkaPublicApiCheck --rerun-tasks","handlingStrategy":"validation","validationCode":"// Keep javadoc HTML and @InterfaceAudience.Public in sync; run warn-only locally\ntasks.named<org.apache.kafka.gradle.KafkaPublicApiCheckerTask>(\"kafkaPublicApiChecker\").configure {\n    failOnViolation = (System.getenv(\"CI\") != null)\n}\n// Quick consistency grep: every type documented should carry @InterfaceAudience.Public\n// rg -l '@InterfaceAudience.Public' src/main | sort  vs.  rg -l '<h[12]' build/docs/javadoc","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When adding a public type, update its javadoc HTML and the @InterfaceAudience.Public annotation together — the checker flags drift between the two.","Open the report path named in the message; each violation names the symbol and whether it is missing from javadoc or from the annotation set.","Use @SuppressKafkaInternalApiUsage(reason = \"...\") for documented exceptions, and keep every suppression justified (the build warns on reason-less ones)."],"tags":["gradle","kafka","api-checker","public-api","javadoc"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}