JDK-8371309 : Diagnostic.getEndPosition can throw an NPE with typical broken code
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 26
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2025-11-04
  • Updated: 2025-11-24
  • Resolved: 2025-11-24
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 26
26 masterFixed
Related Reports
Causes :  
Description
ADDITIONAL SYSTEM INFORMATION :
java -version:
openjdk version "26-beta" 2026-03-17
OpenJDK Runtime Environment Temurin-26+21-202510232033 (build 26-beta+21-ea)
OpenJDK 64-Bit Server VM Temurin-26+21-202510232033 (build 26-beta+21-ea, mixed mode, sharing)

A DESCRIPTION OF THE PROBLEM :
On JDK 26 EAs, calling javax.tools.Diagnostic.getEndPosition in a javax.tools.DiagnosticListener can result in a NullPointerException, attached as "Actual Result" below. This should not happen, like on 25 or earlier.

REGRESSION : Last worked in version 25.0.1

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Put the test case code in `CustomDiagnosticCompiler.java` and run `java CustomDiagnosticCompiler.java`.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The output on 25 was "diagnostic end pos: 33"

I'm not sure if it should be 33 or not, but I would expect it to at least not throw and return Diagnostic.NOPOS instead.
ACTUAL -
Exception in thread "main" java.lang.RuntimeException: java.lang.NullPointerException: Cannot invoke "com.sun.tools.javac.tree.EndPosTable.getEndPos(com.sun.tools.javac.tree.JCTree)" because "endPosTable" is null
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.invocationHelper(JavacTaskImpl.java:168)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:100)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:94)
	at CustomDiagnosticCompiler.main(CustomDiagnosticCompiler.java:24)
Caused by: java.lang.NullPointerException: Cannot invoke "com.sun.tools.javac.tree.EndPosTable.getEndPos(com.sun.tools.javac.tree.JCTree)" because "endPosTable" is null
	at jdk.compiler/com.sun.tools.javac.tree.TreeInfo.getEndPos(TreeInfo.java:646)
	at jdk.compiler/com.sun.tools.javac.tree.JCTree.getEndPosition(JCTree.java:518)
	at jdk.compiler/com.sun.tools.javac.util.JCDiagnostic.getIntEndPosition(JCDiagnostic.java:750)
	at jdk.compiler/com.sun.tools.javac.util.JCDiagnostic.getEndPosition(JCDiagnostic.java:765)
	at jdk.compiler/com.sun.tools.javac.api.ClientCodeWrapper$DiagnosticSourceUnwrapper.getEndPosition(ClientCodeWrapper.java:827)
	at CustomDiagnosticCompiler.lambda$main$0(CustomDiagnosticCompiler.java:20)
	at jdk.compiler/com.sun.tools.javac.api.ClientCodeWrapper$WrappedDiagnosticListener.report(ClientCodeWrapper.java:784)
	at jdk.compiler/com.sun.tools.javac.util.Log.writeDiagnostic(Log.java:1008)
	at jdk.compiler/com.sun.tools.javac.util.Log$DefaultDiagnosticHandler.reportReady(Log.java:974)
	at jdk.compiler/com.sun.tools.javac.util.Log$DiagnosticHandler.reportWithLint(Log.java:190)
	at jdk.compiler/com.sun.tools.javac.util.Log$DiagnosticHandler.report(Log.java:162)
	at jdk.compiler/com.sun.tools.javac.util.Log.report(Log.java:837)
	at jdk.compiler/com.sun.tools.javac.util.AbstractLog.error(AbstractLog.java:125)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.reportSyntaxError(JavacParser.java:502)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.syntaxError(JavacParser.java:466)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.syntaxError(JavacParser.java:460)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.syntaxError(JavacParser.java:456)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.illegal(JavacParser.java:541)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.illegal(JavacParser.java:548)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.term3(JavacParser.java:1772)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.term2(JavacParser.java:1213)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.term1(JavacParser.java:1184)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.term(JavacParser.java:1140)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.term(JavacParser.java:1120)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.unannotatedType(JavacParser.java:1100)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.unannotatedType(JavacParser.java:1096)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.parseType(JavacParser.java:1072)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.parseType(JavacParser.java:1068)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.parseType(JavacParser.java:1063)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.classDeclaration(JavacParser.java:4393)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.classOrRecordOrInterfaceOrEnumDeclaration(JavacParser.java:4356)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.typeDeclaration(JavacParser.java:4345)
	at jdk.compiler/com.sun.tools.javac.parser.JavacParser.parseCompilationUnit(JavacParser.java:4141)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.parse(JavaCompiler.java:662)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.parse(JavaCompiler.java:638)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.parse(JavaCompiler.java:700)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.parseFiles(JavaCompiler.java:1046)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler$InitialFileParser.parse(JavaCompiler.java:2044)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.parseFiles(JavaCompiler.java:1033)
	at jdk.compiler/com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:955)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.lambda$doCall$0(JavacTaskImpl.java:104)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.invocationHelper(JavacTaskImpl.java:152)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.doCall(JavacTaskImpl.java:100)
	at jdk.compiler/com.sun.tools.javac.api.JavacTaskImpl.call(JavacTaskImpl.java:94)
	at CustomDiagnosticCompiler.main(CustomDiagnosticCompiler.java:24)
	at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
	at java.base/java.lang.reflect.Method.invoke(Method.java:565)
	at jdk.compiler/com.sun.tools.javac.launcher.SourceLauncher.execute(SourceLauncher.java:256)
	at jdk.compiler/com.sun.tools.javac.launcher.SourceLauncher.run(SourceLauncher.java:138)
	at jdk.compiler/com.sun.tools.javac.launcher.SourceLauncher.main(SourceLauncher.java:76)

---------- BEGIN SOURCE ----------
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;

void main() {
    SimpleJavaFileObject fileObject = new SimpleJavaFileObject(
        URI.create("string:///BrokenClass.java"), JavaFileObject.Kind.SOURCE
    ) {
        @Override public CharSequence getCharContent(boolean ignoreEncodingErrors) {
            return """
                   public class BrokenClass extends {
                   }
                   """;
        }
    };
    ToolProvider.getSystemJavaCompiler().getTask(
        null,
        null,
        diagnostic -> System.out.println("diagnostic end pos: " + diagnostic.getEndPosition()),
        null,
        null,
        List.of(fileObject)
    ).call();
}
---------- END SOURCE ----------


Comments
Changeset: 43af7b59 Branch: master Author: Jan Lahoda <jlahoda@openjdk.org> Date: 2025-11-24 05:55:48 +0000 URL: https://git.openjdk.org/jdk/commit/43af7b59765fa9820726de276bae9d1fcd2ba3ca
24-11-2025

A pull request was submitted for review. Branch: master URL: https://git.openjdk.org/jdk/pull/28316 Date: 2025-11-14 08:22:46 +0000
14-11-2025

Caused by this change in commit c793de989facdb53: @@ -646,11 +646,6 @@ public static int getEndPos(JCTree tree, EndPosTable endPosTable) { if (tree == null) return Position.NOPOS; - if (endPosTable == null) { - // fall back on limited info in the tree - return endPos(tree); - } - int mapPos = endPosTable.getEndPos(tree); if (mapPos != Position.NOPOS) return mapPos; I believe the intent there was that this method should never be invoked with a null EndPosTable. Obviously we found a case where that's not true. This should fix it: --- i/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java +++ w/src/jdk.compiler/share/classes/com/sun/tools/javac/util/JCDiagnostic.java @@ -747,7 +747,8 @@ protected int getIntPosition() { } protected int getIntEndPosition() { - return (position == null ? Position.NOPOS : position.getEndPosition(source.getEndPosTable())); + return position == null || source.getEndPosTable() == null ? + Position.NOPOS : position.getEndPosition(source.getEndPosTable()); } @DefinedBy(Api.COMPILER)
05-11-2025

I -> H (Regression) L -> L (Uncommon use case) W -> M (Use previous java version) Priority -> HLM -> P3
05-11-2025

Observations on macOS 15.7.1: - JDK 8u471: Passed, message "diagnostic end pos: 33" printed - JDK 25.0.1: Passed, message "diagnostic end pos: 33" printed - JDK 26-ea+22: Failed, Exception thrown. openjdk version "26-ea" 2026-03-17 OpenJDK Runtime Environment (build 26-ea+22-2263) OpenJDK 64-Bit Server VM (build 26-ea+22-2263, mixed mode, sharing) Exception in thread "main" java.lang.RuntimeException: java.lang.NullPointerException: Cannot invoke "com.sun.tools.javac.tree.EndPosTable.getEndPos(com.sun.tools.javac.tree.JCTree)" because "endPosTable" is null
05-11-2025