BerriAI/litellm · error · ValueError

OCR response pages_processed is None

Error message

OCR response pages_processed is None

What it means

For per-page OCR pricing, usage_info.pages_processed must be set. If credits-based pricing didn't apply (no credits or no ocr_cost_per_credit) and pages_processed is None, the calculator raises ValueError rather than silently returning zero cost — missing usage is surfaced as an error except in the credit-model case, which logs a warning and returns 0.0.

Source

Thrown at litellm/cost_calculator.py:1855

    if model_info is not None:
        ocr_cost_per_page = model_info.get("ocr_cost_per_page")

    pages_processed: Final = response.usage_info.pages_processed
    if pages_processed is None:
        if cost_per_credit is not None or ocr_cost_per_page is None:
            # Surface missing usage data instead of silently under-reporting
            # cost. The previous behavior raised ValueError; we now return 0.0
            # for credit-priced or unpriced models, so log a warning to keep
            # the regression visible to operators.
            verbose_logger.warning(
                "OCR cost: model=%s custom_llm_provider=%s response.usage_info."
                "pages_processed is None and credits=%s; returning 0.0 cost.",
                model,
                custom_llm_provider,
                credits,
            )
            return 0.0, 0.0
        raise ValueError("OCR response pages_processed is None")

    if ocr_cost_per_page is None:
        # No per-page pricing configured. Either the model is on credit-based
        # pricing (and credits weren't returned, so the credit branch above did
        # not match) or the model has no OCR pricing entry at all. Surface a
        # warning so that missing pricing entries are visible rather than
        # silently producing zero cost for billable usage.
        verbose_logger.warning(
            "OCR cost: model=%s custom_llm_provider=%s reported "
            "pages_processed=%s but no ocr_cost_per_page is configured; "
            "returning 0.0 cost.",
            model,
            custom_llm_provider,
            pages_processed,
        )
        return 0.0, 0.0

    total_ocr_processing_cost: Final[float] = ocr_cost_per_page * pages_processed

View on GitHub (pinned to 6c2dcb801b)

Solutions

  1. Map pages_processed in your OCR transformation whenever the provider reports it.
  2. Align the model's pricing entry: either supply usage pages for ocr_cost_per_page models or add ocr_cost_per_credit for credit-priced models.
  3. If the provider returns only credits, register ocr_cost_per_credit via litellm.register_model so the credit branch handles it.
  4. Treat missing usage as a provider bug — capture the raw response to confirm what it reports.

Example fix

# before
usage = OCRUsageInfo(credits=None)  # pages_processed omitted

# after
usage = OCRUsageInfo(pages_processed=provider_resp["pages"], credits=provider_resp.get("credits"))
Defensive patterns

Strategy: validation

Validate before calling

info = litellm.get_model_info(model=model, custom_llm_provider=custom_llm_provider) or {}
per_page = info.get("ocr_cost_per_page")
per_credit = info.get("ocr_cost_per_credit")
credits = getattr(response.usage_info, "credits", None) if response.usage_info else None
pages = getattr(response.usage_info, "pages_processed", None) if response.usage_info else None
if pages is None and not (credits is not None and per_credit is not None):
    raise MissingOcrUsage(model)

Type guard

def ocr_priceable(resp, model: str) -> bool:
    ui = getattr(resp, "usage_info", None)
    if ui is None:
        return False
    if getattr(ui, "pages_processed", None) is not None:
        return True
    return getattr(ui, "credits", None) is not None

Try / catch

try:
    cost = ocr_cost_fn(response=resp, model=model)
except ValueError as e:
    if "pages_processed is None" in str(e):
        log_underreported_ocr(model)
        cost = 0.0
    else:
        raise

Prevention

When it happens

Trigger: OCRResponse with usage_info present but pages_processed=None and no usable credits; calling the OCR cost calculator on a response whose usage only carries credits while the model is priced per page (no ocr_cost_per_credit in model_info).

Common situations: Mixed pricing models (credit-based provider, per-page model entry); transformations that map credits but not page counts; new OCR models whose usage schema differs.

Related errors


AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15). Data as JSON: /api/errors/887d2827734f573c. Report an issue: GitHub.