OpenOCR now catches empty results from Gemini 3.1 Flash-Lite, and from any engine that follows
By OpenOCR Team
October 8, 2026. OpenOCR now detects when an engine reads your page and returns nothing. You get a clear error that says why, instead of an empty result that looks like a success. You never pay more than the provider charged for the attempt, and in a multi-page PDF every page that did read is kept.
The pattern began with Gemini 3.1 Flash-Lite, but the protection applies to every engine on OpenOCR.
What we caught
Our monitoring flagged a pattern on Google's Gemini 3.1 Flash-Lite, one of the most-used engines on OpenOCR. On a small share of real documents (forms, cards and letters with clear printed text) it returned no text at all, while Google's API reported the request as successful. Over the past 30 days that happened on about 3 percent of Flash-Lite pages, and it accounted for nearly all the empty results we saw across the platform: about 97 percent.
Three percent is easy to miss in a handful of requests. At document scale, it means a workflow can receive plenty of good text while losing readable pages. The provider's success signal made those losses look like completed jobs.
Retrying didn't help much. The same image came back empty 80 to 93 percent of the time, so repeating the request mostly cost time.
For a refusal with a stated reason, the next step is a different engine.
What the response looks like now
Before this change, a readable page could produce a successful job with no extracted text. Here is a shortened example showing the relevant fields:
{
"status": "succeeded",
"extracted_text": ""
}
Now a provider refusal is explicit. Here is a shortened example where Google billed us for the attempt, so the request carries that cost and nothing more:
{
"status": "failed_charged",
"error_code": "provider_refused",
"cost_debited": 0.000327,
"provider_error": "Google declined to read this image (reason: SAFETY). Sending it to this engine again will give the same result; try a different engine."
}
The job no longer claims usable OCR text was delivered. Billing follows the provider's actual charge, as described below.
Built for every engine, not just one
Gemini was where we caught it, but nothing about this failure is specific to Google. Any vision model can do it, and a few other engines showed it occasionally. So instead of patching one engine, we changed how OpenOCR treats an empty answer from any engine:
A clear answer instead of a blank one. An empty result is reported as a failure with one of two codes:
provider_refused: the provider declined and told us why, for example a safety filter. The error names the reason and tells you that sending the page to the same engine again will give the same result.provider_empty: the model returned nothing and gave no reason. OpenOCR reads the page once more automatically before reporting it.
The codes let your application distinguish a known refusal from an unexplained empty answer. OpenOCR makes the extra attempt for the latter before returning the failure.
Fair billing. If the provider billed us for the attempt, you pay that exact cost, with no markup and never more than your quote. If it didn't, you pay nothing. That's the same as calling the provider directly.
PDFs keep their good pages. If one page of a 30-page PDF comes back empty, you still get the other 29, and the empty page is marked with its page number. A document only fails if no page returned any text.
Consider a 30-page scan sent to Flash-Lite. Pages 1 through 11 return text, page 12 comes back empty, and pages 13 through 30 return text. OpenOCR returns the 29 successful pages with a warning naming page 12. You can send that page to another engine or check it manually. The charge never exceeds the quote.
Every engine is watched. We now track each engine's empty-result rate continuously, so if another model starts doing this we'll see it within the hour.
Gemini explains itself now
Gemini 3.1 Flash-Lite now runs on Google's native API. The endpoint we used before reported these refusals only as "other". The native API says why a page was declined, and that reason now reaches you in the error.
Stalled requests also give up after 60 seconds instead of three minutes, so you can switch engines sooner. Normal Flash-Lite latency is about 3.6 seconds, and its 99th percentile is about 12 seconds. A 180-second timeout held stalled requests far longer than those observed times; 60 seconds still leaves room beyond the usual range.
How we watch for this now
We track each engine's empty-result rate hourly and alert the team when it reaches 5 percent. An overall success rate can stay healthy while one model returns blank answers. This per-engine signal shows us where to investigate.
For developers
Nothing in your integration breaks. You simply stop receiving empty successes. If you retry failures, check the error code: route a provider_refused page to a different engine rather than repeating it. With dozens of engines behind one OpenOCR API key and one balance, that's a one-word change to the engine field.
For PDFs, keep the page warning alongside the returned text so you can identify the gap and decide what to do with that page.
Questions you may have
Will my integration break? No. A page with no extracted text no longer looks like a successful OCR result. If your code retries every failure, use error_code to avoid repeating a provider_refused request on the same engine.
Do I pay for refused pages? Only to the extent the provider billed OpenOCR for the attempt. You pay that exact cost, without markup and never more than the quote. If the provider did not bill us, you pay nothing.
Why not automatically switch engines? You chose the model on purpose. Another engine may have a different price or output. We tell you what happened and let you decide.
Which engines are affected? We found the pattern mainly on Gemini 3.1 Flash-Lite, which accounted for about 97 percent of the platform's empty results over the past 30 days. A few other engines showed it occasionally. The detection and reporting now cover every engine.
What should I do with blank separator pages? Handle known separators as part of your document workflow. For blank sheets or the backs of scans, use a dedicated OCR engine for those pages. We are also testing when vision models invent text on them.
Next: measuring invented text
While testing this release we gave Gemini 3.1 Flash-Lite a completely blank white page. It didn't return nothing. All four times, it invented text: a paragraph about business, a maths proof, a customer letter, and "THE END OF THE CHAPTER". That happens on both of Google's endpoints, so it's the model itself.
An engine that makes up text is harder to catch than one that returns nothing, because the output looks real. We're adding a blank-page test called HallucinationBench to the OpenOCR benchmark: a public test of which OCR models see text on a blank page. Results are coming to the OpenOCR rankings with a ghost icon, so every engine's rate of invented text can be measured and published next to its accuracy. Until then, if your documents include blank sheets, such as separator pages or the backs of scans, use a dedicated OCR engine for those pages.