tonhowtf/omniget · error

não achei o catálogo (/Root) do documento

Error message

não achei o catálogo (/Root) do documento

What it means

When rebuilding the xref table, `rebuild_xref` must emit a trailer containing a `/Root` reference to the document catalog. If the existing trailer lacks `/Root` and neither `find_root_ref` (a `2 0 R`-style ref) nor `find_catalog` (an `obj` with `/Type /Catalog`) locates the catalog, reconstruction is impossible and this error is thrown.

Solutions

  1. Search the file for `/Type /Catalog`; if found but unreferenced, the heuristic failed — check whether the object number is unusual (e.g. very large) and consider patching `find_catalog`'s matching
  2. Verify the file is complete — a truncated tail loses both trailer and xref; re-download or re-export the source
  3. Try recovering the catalog from incremental updates: an earlier revision of the file may contain a valid `/Root` reference
  4. Use a third-party recovery tool (e.g. qpdf/mutool clean) to rebuild structure before this repair

Example fix

// before
let (bytes, objs) = rebuild_xref(&data)?;
// after
if !data.windows(15).any(|w| w == b"/Type /Catalog") {
    eprintln!("no catalog object present — rebuild_xref will fail");
    return Ok(());
}
let (bytes, objs) = rebuild_xref(&data)?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_catalog(data: &[u8]) -> bool {
    data.windows(14).any(|w| w == b"/Type /Catalog")
        || data.windows(5).any(|w| w == b"/Root")
}
// require has_catalog(&data) before calling rebuild_xref on trailer-damaged files

Try / catch

match rebuild_xref(&data) {
    Err(e) if e.to_string().contains("/Root") => {
        eprintln!("catalog missing — file is too damaged for xref-only repair; use a full recovery tool");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Repairing a PDF whose trailer dictionary is missing or truncated and whose catalog object is absent, renumbered in an unrecognized way, or unreferenced — so `run`/`rebuild_xref` cannot determine the document root.

Common situations: Files cut off mid-trailer by truncation; PDFs assembled by tools that omit the trailer (linearized/exotic writers); prior failed edits that dropped or renumbered the catalog object; files recovered from disk with partially overwritten object streams.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12). Data as JSON: /api/errors/06105976eddd87a9. Report an issue: GitHub.

Appendix: source

Thrown at src-tauri/omniget-core/src/core/tools/pdf_repair.rs:390

            let off = out.len();
            out.extend_from_slice(format!("{} 0 obj\n", num).as_bytes());
            out.extend_from_slice(&content);
            out.extend_from_slice(b"\nendobj\n");
            by_num.insert(num, (off, 0));
            unpacked += 1;
        }
    }
    tracing::debug!("[pdf-repair] {} objetos soltos de object stream", unpacked);

    let max = by_num.keys().copied().max().unwrap_or(0);
    let size = max + 1;
    let all: Vec<(u32, usize, u16)> = by_num.iter().map(|(n, (o, g))| (*n, *o, *g)).collect();
    let trailer = match last_trailer_dict(body) {
        Some(d) if d.contains("/Root") => patch_trailer(&d, size),
        _ => {
            let root = find_root_ref(&out)
                .or_else(|| find_catalog(&out, &all))
                .ok_or_else(|| anyhow!("não achei o catálogo (/Root) do documento"))?;
            format!("<< /Size {} /Root {} >>", size, root)
        }
    };

    let xref_at = out.len();
    out.extend_from_slice(format!("xref\n0 {}\n", size).as_bytes());
    out.extend_from_slice(b"0000000000 65535 f \n");
    for n in 1..size {
        match by_num.get(&n) {
            Some((off, gen)) => {
                out.extend_from_slice(format!("{:010} {:05} n \n", off, gen).as_bytes())
            }
            None => out.extend_from_slice(b"0000000000 65535 f \n"),
        }
    }
    out.extend_from_slice(b"trailer\n");
    out.extend_from_slice(trailer.trim().as_bytes());
    out.extend_from_slice(format!("\nstartxref\n{}\n%%EOF\n", xref_at).as_bytes());

View on GitHub (pinned to 8600b91f42)