{"id":"e54f4969b35141d8","repo":"apache/kafka","slug":"found-d-internal-api-usage-violations-see-report","errorCode":null,"errorMessage":"Found %d internal API usage violations. See report: %s","messagePattern":"Found (.+?) internal API usage violations\\. See report: (.+?)","errorType":"exception","errorClass":"GradleException","httpStatus":null,"severity":"error","filePath":"api-checker/gradle-plugins/src/main/java/org/apache/kafka/gradle/KafkaInternalApiCheckerTask.java","lineNumber":148,"sourceCode":"        reporter.writeTextReport(violations, suppressions, report);\n        reporter.printToConsole(violations, suppressions);\n\n        getLogger().info(\"Internal API usage check completed. Report written to: {}\", report.getAbsolutePath());\n\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            getLogger().info(\"No internal API usage found.\");\n            return;\n        }\n\n        String message = String.format(\"Found %d internal API usage violations. See report: %s\",\n                violations.size(), report.getAbsolutePath());\n        if (failOnViolation.get()) {\n            throw new GradleException(message);\n        }\n        getLogger().warn(message);\n    }\n\n    @Input\n    public Property<Boolean> getCheckerEnabled() {\n        return enabled;\n    }\n\n    @Input\n    public Property<Boolean> getFailOnViolation() {\n        return failOnViolation;\n    }\n\n    @Input\n    public Property<Boolean> getFailOnNoKafkaDependency() {\n        return failOnNoKafkaDependency;\n    }","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/api-checker/gradle-plugins/src/main/java/org/apache/kafka/gradle/KafkaInternalApiCheckerTask.java#L130-L166","documentation":"Thrown by KafkaInternalApiCheckerTask.reportResults() when the bytecode scan found one or more internal Kafka API references in the project's compiled classes and failOnViolation is true (its default). Each violation corresponds to a class in build/classes that references a Kafka type not annotated @InterfaceAudience.Public, i.e. a package-private or internal-API usage that is not safe for external consumers. The report path in the message points to the human-readable breakdown of every violation and honoured suppression.","triggerScenarios":"reportResults() at line 148: violations.isEmpty() is false and failOnViolation.get() is true. Produced when compiled project bytecode references internal Kafka symbols (e.g. org.apache.kafka.common.utils.Bytes, anything under impl packages, or non-@InterfaceAudience.Public classes) and no @SuppressKafkaInternalApiUsage annotation justifies the usage.","commonSituations":"Code reaches into Kafka internals (utils, protocol classes, *Impl) because the public API lacks a needed method; an upgrade to a newer Kafka release removed @InterfaceAudience.Public from a previously-public symbol; copy-pasted sample code that uses internal helpers; a refactor that promotes an internal class into a public signature without re-annotating it.","solutions":["Open the report at the path in the message and read each violation to see which internal symbol is referenced and from where.","Replace the internal-API call with the supported public-API equivalent (e.g. use org.apache.kafka.clients.consumer.ConsumerRecord fields instead of internal helpers).","If the usage is genuinely necessary and justified, annotate the offending element with @SuppressKafkaInternalApiUsage(reason = \"...\") per KIP-1265 — and make sure the reason text is non-empty, otherwise the build still warns about unjustified suppressions.","If the check is producing false positives during a migration, set kafkaInternalApiChecker.failOnViolation = false temporarily to keep the build green while the report is triaged (do not leave it off)."],"exampleFix":"// before\nimport org.apache.kafka.common.utils.Utils;\n\nclass MyClient {\n  int port = Utils.portFromUri(uri); // internal API -> violation\n}\n\n// after — use the public API\nimport org.apache.kafka.clients.CommonClientConfigs;\n\nclass MyClient {\n  int port = Integer.parseInt(config.getString(CommonClientConfigs.PORT_CONFIG));\n}","handlingStrategy":"validation","validationCode":"// Gate violations only in CI; warn in local dev so you can triage before the build breaks\ntasks.named<org.apache.kafka.gradle.KafkaInternalApiCheckerTask>(\"kafkaInternalApiChecker\").configure {\n    failOnViolation = (System.getenv(\"CI\") != null)\n}\n// Editor/CI pre-scan for known internal package imports before invoking the plugin\n// rg -n 'org\\.apache\\.kafka\\.[A-Za-z0-9_.]+\\.internal' src/main","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only reference types annotated @InterfaceAudience.Public; treat any *.internal.* package as off-limits.","When an internal usage is intentional and justified, suppress it with @SuppressKafkaInternalApiUsage(reason = \"...\") per KIP-1265 — never edit bytecode to hide it.","Read the report path printed in the message; it lists each violating class/symbol so fixes are mechanical."],"tags":["gradle","kafka","api-checker","internal-api","violation"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}