opendatalab/MinerU · error · ValueError

middle_json must be a dict.

Error message

middle_json must be a dict.

What it means

ValueError from regenerate_client_side_outputs(): after json.loads of the middle json, the top-level value must be a dict (the code immediately reads .get('_backend') and .get('pdf_info')). A JSON array, string, number, or null at the top level fails this check.

Source

Thrown at mineru/cli/client_side_output.py:60


def regenerate_client_side_outputs(
    parse_dir: str | Path,
    doc_stem: str,
) -> tuple[Path, ...]:
    """读取服务端 staged/finalized middle json,并在客户端覆盖生成最终输出产物。"""
    parse_dir = Path(parse_dir)
    middle_json_path = parse_dir / f"{doc_stem}_middle.json"
    markdown_path = parse_dir / f"{doc_stem}.md"
    content_list_path = parse_dir / f"{doc_stem}_content_list.json"
    content_list_v2_path = parse_dir / f"{doc_stem}_content_list_v2.json"

    if not middle_json_path.exists():
        raise FileNotFoundError(f"Missing middle json file: {middle_json_path}")

    middle_json = json.loads(middle_json_path.read_text(encoding="utf-8"))
    if not isinstance(middle_json, dict):
        raise ValueError("middle_json must be a dict.")
    backend = middle_json.get("_backend")
    pdf_info = middle_json.get("pdf_info")
    if backend not in SUPPORTED_BACKENDS:
        raise ValueError(
            f"Unsupported middle json backend for client-side output generation: {backend}"
        )
    if not isinstance(pdf_info, list):
        raise ValueError("middle_json must contain a list field named pdf_info.")

    if backend in PDF_BACKENDS:
        finalize_client_side_middle_json(middle_json)
        pdf_info = middle_json["pdf_info"]

    make_func = _select_union_make(backend)
    image_dir = "images"

    markdown_path.write_text(
        make_func(pdf_info, MakeMode.MM_MD, image_dir),

View on GitHub (pinned to 4fe4bde114)

Solutions

  1. Regenerate the middle json with a current mineru server so it is wrapped in an object with _backend and pdf_info keys
  2. If producing middle json yourself, emit {"_backend": "pipeline", "pdf_info": [...]}
  3. Validate the shape in a pre-check before calling regenerate (see defense below)

Example fix

# before: middle.json contains just [ {...page1...}, {...page2...} ]

# after: {"_backend": "pipeline", "pdf_info": [ {...page1...}, {...page2...} ]}
Defensive patterns

Strategy: type-guard

Validate before calling

payload = json.loads(middle_json_path.read_text(encoding="utf-8"))
if not isinstance(payload, dict):
    raise SystemExit("middle json schema mismatch: top level must be an object; regenerate with current mineru")

Type guard

def is_middle_json_dict(payload: object) -> bool:
    return isinstance(payload, dict)

Try / catch

try:
    regenerate_client_side_outputs(parse_dir, doc_stem)
except ValueError as e:
    if "must be a dict" in str(e):
        refetch_results()  # schema is foreign/stale; do not try to patch it
    else:
        raise

Prevention

When it happens

Trigger: A {doc}_middle.json whose top level is a list (e.g. a bare pdf_info array serialized by an old version or a custom exporter) or a truncated/corrupted file that still parses as valid non-dict JSON (rare); most corruption instead raises JSONDecodeError one line earlier.

Common situations: Hand-rolled middle-json producers that dump the page array directly; third-party tools writing their own _middle.json; files from very old mineru versions with a different schema.

Related errors


AI-assisted analysis of opendatalab/MinerU@4fe4bde114 (2026-08-14). Data as JSON: /api/errors/23fe89ac8684e1fc. Report an issue: GitHub.