spring-projects/spring-ai · error · RuntimeException
IOException (wrapped RuntimeException)
Error message
IOException (wrapped RuntimeException)
What it means
JsonReader.get() parses the underlying resource as JSON; if reading/parsing the stream throws IOException (unreadable resource, I/O error mid-stream), it is wrapped in a plain RuntimeException. Malformed JSON that Jackson reports as IOException variants (e.g. JsonParseException extends IOException) also lands here.
Solutions
- Verify resource.exists() and that the content is valid JSON before constructing JsonReader
- Validate the JSON with a standalone parse (JsonMapper.shared().readTree) to get richer error details
- Catch RuntimeException and inspect the cause for the real IOException/JsonParseException
- Log resource.getDescription() to confirm which path was actually resolved
Example fix
// before
List<Document> docs = new JsonReader(resource).get(); // RuntimeException on bad content
// after
if (!resource.exists()) throw new IllegalStateException("Missing: " + resource);
JsonMapper.shared().readTree(resource.getInputStream()); // fail early with a clear parse error
List<Document> docs = new JsonReader(resource).get(); Defensive patterns
Strategy: validation
Validate before calling
if (!resource.exists()) throw new IllegalStateException("Missing JSON: " + resource);
try (InputStream in = resource.getInputStream()) {
JsonMapper.shared().readTree(in); // fail fast with a precise Jackson error
} Try / catch
try {
docs = jsonReader.get();
} catch (RuntimeException e) {
throw new IllegalStateException("JSON read failed: " + resource, e.getCause());
} Prevention
- Validate JSON files with a linter before ingestion
- Check exists()/isReadable() on the resource
- Keep resources on stable local storage rather than flaky remote URLs
When it happens
Trigger: Calling jsonReader.get() where the Resource input stream fails — file missing, permission denied, connection dropped — or content that Jackson cannot parse (invalid JSON raising JsonParseException).
Common situations: Pointing JsonReader at a non-JSON file, truncated downloads, or resources inside a jar with wrong classpath path.
Understand the failure class
Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.
Related errors
- Error reading JSON resource
- Failed to get index size for session:
- IOException (wrapped RuntimeException)
- Cannot deserialize ThinkOption from token:
- Conversion from JSON failed
AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11).
Data as JSON: /api/errors/65b7877f8e4e3630.
Report an issue: GitHub.
Appendix: source
Thrown at spring-ai-commons/src/main/java/org/springframework/ai/reader/JsonReader.java:86
this.jsonKeysToUse = List.of(jsonKeysToUse);
}
@Override
public List<Document> get() {
try {
JsonNode rootNode = JsonMapper.shared().readTree(this.resource.getInputStream());
if (rootNode.isArray()) {
return StreamSupport.stream(rootNode.spliterator(), true)
.map(jsonNode -> parseJsonNode(jsonNode, JsonMapper.shared()))
.toList();
}
else {
return Collections.singletonList(parseJsonNode(rootNode, JsonMapper.shared()));
}
}
catch (IOException e) {
throw new RuntimeException(e);
}
}
private Document parseJsonNode(JsonNode jsonNode, JsonMapper jsonMapper) {
Map<String, Object> item = jsonMapper.convertValue(jsonNode, new TypeReference<>() {
});
var sb = new StringBuilder();
this.jsonKeysToUse.stream()
.filter(item::containsKey)
.forEach(key -> sb.append(key).append(": ").append(item.get(key)).append(System.lineSeparator()));
Map<String, Object> metadata = this.jsonMetadataGenerator.generate(item);
String content = sb.isEmpty() ? item.toString() : sb.toString();
return new Document(content, metadata);
}
View on GitHub (pinned to 98a7beda4f)