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
MemoryPageImageCache.getOrFetch() delegates to a PageImageFetcher callback when a page image is not in the cache. If the fetcher returns null (rather than throwing an exception), the cache treats it as a programming error — a null BufferedImage cannot be cached or processed downstream. This guard prevents a silent NPE later in the pipeline when code tries to use the image.
Source
Thrown at java/opendataloader-pdf-core/src/main/java/org/opendataloader/pdf/hybrid/MemoryPageImageCache.java:37
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* In-memory page image cache. Stores images in a HashMap; evict() removes
* entries so GC can reclaim memory (~25MB per page image).
*/
public class MemoryPageImageCache implements PageImageCache {
private final Map<Integer, BufferedImage> cache = new HashMap<>();
@Override
public BufferedImage getOrFetch(int pageIndex, PageImageFetcher fetcher) throws IOException {
BufferedImage image = cache.get(pageIndex);
if (image == null) {
image = fetcher.fetch(pageIndex);
if (image == null) {
throw new IOException("Page image fetcher returned null for page " + pageIndex);
}
cache.put(pageIndex, image);
}
return image;
}
@Override
public void evict(int pageIndex) {
cache.remove(pageIndex);
}
@Override
public void close() {
cache.clear();
}
}
View on GitHub (pinned to a7789b8e77)
Solutions
- Fix the PageImageFetcher implementation to throw IOException on failure instead of returning null.
- Wrap the fetcher: `cache.getOrFetch(pageIndex, idx -> { BufferedImage img = rawFetcher.fetch(idx); if (img == null) throw new IOException("Render returned null for page " + idx); return img; })`.
- Check whether the page is renderable before calling getOrFetch (e.g., verify the page has content and a valid media box).
- Log the pageIndex to identify which page triggers the null — often a specific corrupt page.
Example fix
// before: fetcher returns null on render failure, cache throws opaque error
BufferedImage img = cache.getOrFetch(pageIndex, idx -> {
return renderer.renderImage(idx); // returns null on error
});
// after: fetcher throws IOException on null, cache stays happy
BufferedImage img = cache.getOrFetch(pageIndex, idx -> {
BufferedImage rendered = renderer.renderImage(idx);
if (rendered == null) {
throw new IOException("Renderer returned null for page " + idx);
}
return rendered;
}); Defensive patterns
Strategy: validation
Validate before calling
// Wrap the fetcher to guarantee it never returns null
PageImageFetcher safeFetcher = pageIndex -> {
BufferedImage img = rawFetcher.fetch(pageIndex);
if (img == null) {
throw new IOException("Render returned null for page " + pageIndex);
}
return img;
};
BufferedImage image = cache.getOrFetch(pageIndex, safeFetcher); Type guard
public static boolean isNonNullFetcher(PageImageFetcher fetcher) {
return fetcher != null;
// Cannot statically guarantee fetch() returns non-null — must wrap at runtime.
} Try / catch
try {
image = cache.getOrFetch(pageIndex, safeFetcher);
} catch (IOException e) {
if (e.getMessage().contains("returned null")) {
// Fetcher implementation bug — fix the fetcher, don't retry with same fetcher
LOGGER.severe("PageImageFetcher returned null for page " + pageIndex
+ ". Fix the fetcher implementation to throw IOException on failure.");
throw e;
}
throw e;
} Prevention
- Always wrap PageImageFetcher implementations with a null-check guard that throws IOException.
- Never let a fetcher silently return null — translate null into an exception at the fetch boundary.
- Test fetcher implementations against edge-case pages (blank, corrupt, zero-size) to ensure they throw rather than return null.
- Log the pageIndex when this error occurs to identify problematic pages.
When it happens
Trigger: Calling cache.getOrFetch(pageIndex, fetcher) where the fetcher implementation's fetch() method returns null instead of throwing an IOException on failure. This typically happens when a fetcher wraps a rendering call that returns null on error (e.g., PDFBox renderImageWithDPI returning null in some edge cases) without translating the null into an exception.
Common situations: Custom PageImageFetcher implementation that silently swallows rendering errors and returns null; PDFBox rendering a page with an unsupported color space or corrupt content stream where the renderer returns null rather than throwing; a fetcher that delegates to another cache layer which has been evicted/closed and returns null; a fetcher that hits an error path but forgets to throw.
Related errors
- Page image fetcher returned null for page %s
- Hybrid backend type cannot be null or empty
- Unknown hybrid backend: %s. Supported backends: %s
- Timeout must be non-negative: %s
- Max concurrent requests must be positive: %s
AI-assisted analysis of opendataloader-project/opendataloader-pdf@a7789b8e77 (2026-08-14).
Data as JSON: /api/errors/8975382ec6f4b5d3.
Report an issue: GitHub.