opendataloader-project/opendataloader-pdf · error · IOException
Page image fetcher returned null for page %s
Error message
Page image fetcher returned null for page %s
What it means
DiskPageImageCache.getOrFetch requires the supplied PageImageFetcher.fetch(pageIndex) to return a non-null BufferedImage so it can write the PNG to disk and hand it back. A null return means no image was produced for that page and the cache has nothing to persist or return, so it fails fast with IOException rather than writing an empty file or propagating a null downstream.
Source
Thrown at java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/DiskPageImageCache.java:61
DiskPageImageCache(Path tempDir) {
this.tempDir = tempDir;
}
@Override
public BufferedImage getOrFetch(int pageIndex, PageImageFetcher fetcher) throws IOException {
Path file = tempDir.resolve("page-" + pageIndex + ".png");
if (Files.exists(file)) {
BufferedImage cached = ImageIO.read(file.toFile());
if (cached != null) {
return cached;
}
// Cached file is unreadable (corrupt or no ImageReader) — re-fetch.
LOGGER.log(Level.WARNING, "Cached page image is unreadable, re-fetching: {0}", file);
Files.deleteIfExists(file);
}
BufferedImage image = fetcher.fetch(pageIndex);
if (image == null) {
throw new IOException("Page image fetcher returned null for page " + pageIndex);
}
if (!ImageIO.write(image, "png", file.toFile())) {
throw new IOException("No ImageIO writer accepted PNG output for page " + pageIndex);
}
return image;
}
@Override
public void evict(int pageIndex) {
// no-op: keep on disk for potential re-read
}
@Override
public void close() throws IOException {
if (!Files.exists(tempDir)) {
return;
}
try (DirectoryStream<Path> stream = Files.newDirectoryStream(tempDir)) {View on GitHub (pinned to a7789b8e77)
Solutions
- Make the PageImageFetcher return a non-null BufferedImage for every requested page index
- Throw IOException from the fetcher on failure instead of returning null
- Validate the page-index range against the document before calling getOrFetch
Example fix
// before
BufferedImage fetch(int i) {
if (outOfRange(i)) return null;
...
}
// after
BufferedImage fetch(int i) throws IOException {
if (outOfRange(i)) throw new IOException("page " + i + " out of range");
...
} Defensive patterns
Strategy: try-catch
Try / catch
// For a custom PageImageFetcher, validate the contract yourself
PageImageFetcher safeFetcher = idx -> {
BufferedImage img = realFetcher.fetch(idx);
if (img == null) throw new IOException("no image for page " + idx);
return img;
};
BufferedImage out = cache.getOrFetch(pageIndex, safeFetcher); Prevention
- Never return null from a PageImageFetcher — throw IOException on failure
- Unit-test custom fetchers against the full page range
- Prefer the shipped HancomAIClient.fetchPageImage which throws rather than returns null
When it happens
Trigger: A PageImageFetcher lambda passed to getOrFetch returns null for a page index. In the shipped pipeline the fetcher is HancomAIClient.fetchPageImage, which throws IOException instead of returning null, so this only fires for a custom/modified fetcher or a future code path that adds a null return.
Common situations: Writing a custom PageImageCache or fetcher for tests/stubs; a third-party image source that returns null on unsupported page indices; an OCR/render hook that swallows an error and yields null.
Related errors
- Page image fetcher returned null for page %s
- Unsupported hybrid backend '%s'. Supported values: %s
- Option --hybrid requires a value. Supported values: %s
- Unsupported hybrid backend '%s'. Supported values: %s
- Option --%s: unsupported value '%s'. Supported values: %s, %
AI-assisted analysis of opendataloader-project/opendataloader-pdf@a7789b8e77 (2026-08-14).
Data as JSON: /api/errors/66fc07098ca5875b.
Report an issue: GitHub.