---
title: "Repair a damaged PDF"
url: https://recursiveai.org/onepdf/cookbook/repair-damaged-pdf/
description: "Open a damaged or truncated PDF in C#, see what OnePDF repaired, and save a clean copy, or reject damaged files outright with strict mode."
version: "1.11.0"
---

# Repair a damaged PDF

Open a damaged or truncated PDF in C#, see what OnePDF repaired, and save a clean copy, or reject damaged files outright with strict mode.

The input here is a PDF whose last lines were cut off, and with them `startxref`, the pointer to its cross-reference table.

```csharp
using OnePdf;

// Lenient by default: what can be repaired is repaired, and each repair is recorded.
using (PdfDocument doc = PdfDocument.Open("damaged.pdf"))
{
    Console.WriteLine($"{doc.PageCount} pages, title: {doc.Info.Title}");
    doc.Save("repaired.pdf");                       // a clean file with a new cross-reference table

    foreach (PdfDiagnostic d in doc.Diagnostics)    // severity, object number, offset, message
        Console.WriteLine(d);
}

// Strict: the first problem throws instead.
try
{
    using PdfDocument strict = PdfDocument.Open("damaged.pdf", new PdfOpenOptions { Strict = true });
}
catch (PdfFormatException e)
{
    Console.WriteLine($"strict: {e.Message}");
}

// TryOpen never throws for a file that will not open.
if (PdfDocument.TryOpen("not-a-pdf.txt", out PdfDocument? other, out PdfException? error))
    other.Dispose();
else
    Console.WriteLine($"not-a-pdf.txt: {error.Message}");
```

```text
6 pages, title: Harbor trial report
Warning: The cross-reference data is unusable (There is no startxref near the end of the file.); rebuilt by scanning the file for objects.
strict: The cross-reference data is unusable (There is no startxref near the end of the file.); rebuilt by scanning the file for objects.
not-a-pdf.txt: The file has no usable cross-reference data, trailer or document catalog.
```

`qpdf --check` finds no errors in `repaired.pdf`.

## The repair chain

1. **Trust the cross-reference data.** Read the table or stream that `startxref` names, then the earlier ones.
2. **Fix single objects by scanning.** An entry that doesn’t lead to its object is found by scanning the file for object headers. A stream whose `/Length` is wrong ends at its `endstream`.
3. **Rebuild.** When the cross-reference data can’t be read at all, the whole file is scanned and rebuilt from the objects it holds, as here.

Each repair is a `PdfDiagnostic` on `doc.Diagnostics`, with the severity, the object number and the byte offset when known. Some repairs only happen when an object is first read, so read the list after the work you need, as the sample does after `Save`.

## Good to know

- `Save` writes a clean file: new cross-reference data, correct lengths, no unreachable objects. Content that was truly lost stays lost: a reference to a missing object is written as `null`.
- Don’t use `SaveIncremental` on a repaired file. A file that was rebuilt by scanning has no cross-reference section to append to, so it throws.
- A page that can’t be parsed throws only when you use that page, so one bad page doesn’t stop you reading the others.
- Limits on decoded size, nesting, object count and more are on by default, and are never relaxed by repair: a file that exceeds them throws `PdfLimitException`.
- `Strict = true` turns every repair into a `PdfFormatException`, for pipelines that should reject damaged input.

Every rule is in [Reading and repair rules](https://recursiveai.org/onepdf/docs/repair/).
