/ Documentation / CLI reference / luksbox extract --tolerate-errors

luksbox extract --tolerate-errors

Forensic best-effort extract: recover what is still readable from a partly-corrupted file.

luksbox extract <PATH> <INNER> <LOCAL> --tolerate-errors [--report <PATH>] [--unlock-args]

Pulls a file out of the vault like get, but tolerates per-chunk AEAD failures by writing 4096 zero bytes in place of each unrecoverable chunk and continuing. Prints the chunk_idx and on- disk offset of every failure to stderr (and to --report <path> as JSON, if set).

Use this only when get fails and you want to recover what is still readable from a partly-corrupted file. The output is by definition lossy: damaged chunks become 4 KiB ranges of zeros at the corresponding offsets.

The --tolerate-errors flag is mandatory. Without it, extract refuses to run. This is deliberate: a user running the command on autopilot should never silently capture a zero-padded file thinking it is the real one.

Examples

Single-file lossy recovery

# `get` is strict - fails on any AEAD error
luksbox get my.lbx /payload broken.bin
# error: crypto: AEAD failure

# `extract --tolerate-errors` recovers the rest of the file
luksbox extract my.lbx /payload recovered.bin --tolerate-errors
# chunk_idx=1 chunk_id=1 gen=5 off=1060892 FAILED (crypto: AEAD failure); writing 4096 zero bytes
# wrote 12000 bytes to recovered.bin (2 chunks OK, 1 chunks zero-filled)
# warning: 1 chunk(s) were unrecoverable. The output file has 4096-byte zero ranges
#  at the corresponding offsets.

With a structured failure report

luksbox extract my.lbx /payload recovered.bin --tolerate-errors --report fail.json
cat fail.json
{
  "vault": "my.lbx",
  "inner": "/payload",
  "local": "recovered.bin",
  "bytes_written": 12000,
  "chunks_ok": 2,
  "chunks_bad": 1,
  "failures": [
    {
      "chunk_idx": 1,
      "chunk_id": 1,
      "generation": 5,
      "slot_offset": 1060892,
      "error": "crypto: AEAD failure"
    }
  ]
}

After check --json

A common workflow is to first run check --json to discover which files are affected, then extract each one in turn:

luksbox check my.lbx --json \
  | jq -r '.failures | map(.path) | unique[]' \
  | while read -r vault_path; do
      out=recovered$(echo "$vault_path" | tr / _).bin
      luksbox extract my.lbx "$vault_path" "$out" --tolerate-errors --report "$out.report.json"
    done

Output details

Validating the recovery

If you have an external truth source (a HMAC of the original file, a hash database, the original file itself), validate the recovered file against that. Common patterns:

Truth source Check
You have the original SHA-256 from before damage sha256sum recovered.bin and compare; it will not match if any chunk was zero-filled
The file has internal integrity (e.g. tar with checksums, ZIP central directory, sqlite WAL) Run the format-specific check (e.g. tar tvf -O recovered.bin, unzip -l recovered.bin) and look for which records are readable
The file is a directory archive Use the report's failures[].chunk_idx and the file's chunk-to-byte mapping (4096 bytes per chunk) to identify which logical record falls in the damaged range

Limitations

Failure modes

Failure Cause
extract is the lossy recovery path: pass --tolerate-errors to acknowledge ... The mandatory --tolerate-errors flag was not set
<inner> is not a file Target path resolves to a directory or does not exist
creating <local>: ... Output path could not be created (typically permissions or --no-clobber-style protection)

See the forensics page for the full recovery flow this subcommand fits into.