spring-projects / spring-projects/spring-modulith

Documenter fails with NPE and unhelpful message if Javadoc contains quotation marks

Open
#1,685 6 comments 0 reactions 1 assignee View on GitHub

@odrotbohm is already working on this.

Since May 11, 2026.

in: documentation support meta: waiting for feedback type: bug
Dominant language
Java
Stars
1.2k
Forks
222
PR merge metrics
No merged PRs in 30d

Description

I had a method with example code in the javadoc, where I forgot to escape ".

  /**
   * Findet Gemeinde, Gemarkung und Flur anhand ihrer Fachschluessel.
   *
   * <p>Example:
   *
   * <pre>
   * Tuple3<Gemeinde, Gemarkung, Flur> gemeindeGemarkungUndFlur =
   *     gemeindeRepository.findGemeindeGemarkungFlurByFachschluessel("08117026", "2050", "000") // <<=== double quotes
   *     
   * </pre>
   * /

IDEs no longer complain about that, but the Json Parser in Documenter was unhappy without giving very useful information:

java.lang.ExceptionInInitializerError
	at org.springframework.modulith.docs.Asciidoctor.getSpringModulithDocsSource(Asciidoctor.java:427)
	at org.springframework.modulith.docs.Asciidoctor.<clinit>(Asciidoctor.java:59)
	at org.springframework.modulith.docs.Documenter.toModuleCanvas(Documenter.java:432)
	at org.springframework.modulith.docs.Documenter.lambda$writeModuleCanvases$2(Documenter.java:392)
	at java.base/java.lang.Iterable.forEach(Iterable.java:75)
	at org.springframework.modulith.docs.Documenter.writeModuleCanvases(Documenter.java:388)
	at org.springframework.modulith.docs.Documenter.writeDocumentation(Documenter.java:201)
	at org.springframework.modulith.docs.Documenter.writeDocumentation(Documenter.java:180)
	at de.kommone.kps.vertrag.ApplicationModularityTests.verifyStructure(ApplicationModularityTests.java:13)
	at java.base/java.lang.reflect.Method.invoke(Method.java:580)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1596)
Caused by: org.springframework.boot.json.JsonParseException: Cannot parse JSON
	at org.springframework.boot.json.AbstractJsonParser.tryParse(AbstractJsonParser.java:57)
	at org.springframework.boot.json.BasicJsonParser.parseList(BasicJsonParser.java:51)
	at org.springframework.modulith.docs.SpringModulithDocumentationSource.from(SpringModulithDocumentationSource.java:123)
	at org.springframework.modulith.docs.SpringModulithDocumentationSource.<init>(SpringModulithDocumentationSource.java:62)
	at java.base/java.util.Optional.map(Optional.java:260)
	at org.springframework.modulith.docs.SpringModulithDocumentationSource.<clinit>(SpringModulithDocumentationSource.java:49)
	... 12 more
Caused by: java.lang.NullPointerException: Cannot load from object array because "values" is null
	at org.springframework.boot.json.BasicJsonParser.parseMapInternal(BasicJsonParser.java:84)
	at org.springframework.boot.json.BasicJsonParser.parseInternal(BasicJsonParser.java:71)
	at org.springframework.boot.json.BasicJsonParser.parseListInternal(BasicJsonParser.java:58)
	at org.springframework.boot.json.BasicJsonParser.parseInternal(BasicJsonParser.java:68)
	at org.springframework.boot.json.BasicJsonParser.parseMapInternal(BasicJsonParser.java:87)
	at org.springframework.boot.json.BasicJsonParser.parseInternal(BasicJsonParser.java:71)
	at org.springframework.boot.json.BasicJsonParser.parseListInternal(BasicJsonParser.java:58)
	at org.springframework.boot.json.BasicJsonParser.lambda$parseList$2(BasicJsonParser.java:51)
	at org.springframework.boot.json.AbstractJsonParser.trimParse(AbstractJsonParser.java:46)
	at org.springframework.boot.json.AbstractJsonParser.parseList(AbstractJsonParser.java:40)
	at org.springframework.boot.json.BasicJsonParser.lambda$parseList$3(BasicJsonParser.java:51)
	at org.springframework.boot.json.AbstractJsonParser.tryParse(AbstractJsonParser.java:53)
	... 17 more

It happens in BasicJsonParser, and the stacktrace does make it easy to find the place:

	private Map<String, Object> parseMapInternal(int nesting, String json) {
		Map<String, Object> map = new LinkedHashMap<>();
		json = trimEdges(json, '{', '}').trim();
		for (String pair : tokenize(json)) {
			String[] values = StringUtils.trimArrayElements(StringUtils.split(pair, ":"));
			Assert.state(values[0].startsWith("\"") && values[0].endsWith("\""), // <<===== values is null here
					"Expecting double-quotes around field names");
			String key = trimEdges(values[0], '"', '"');
			Object value = parseInternal(nesting, values[1]);
			map.put(key, value);
		}
		return map;
	}

The json in tokenize(json) was invalid, because the json String contained double quotes around the strings "08117026" etc. so that the comment string contains offending nested double quotes:

{
  "name": "findGemeindeGemarkungFlurByFachschluessel",
  "signature": "findGemeindeGemarkungFlurByFachschluessel(java.lang.String, java.lang.String, java.lang.String)",
  "comment": "Findet Gemeinde, Gemarkung und Flur anhand ihrer Fachschluessel.  <p>Example:  <pre> Tuple3<Gemeinde, Gemarkung, Flur> gemeindeGemarkungUndFlur = gemeindeRepository.findGemeindeGemarkungFlurByFachschluessel("08117026", "2050", "000")</pre>"
}

The fix was to replace the double quotes in the example code of the javadoc by &quot;.

Would it be possible to check if values is null and make sure the offending json becomes part of the error message? or maybe even ensure that Javadoc is escaped before it goes into the Documenter json?

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.