Troubleshooting

Symptoms, likely causes, and verification steps. For problems not listed here, see Exit codes for the error code key and FAQ for design-rationale questions.

"No space left on device"

The extraction filled the output filesystem. Two causes:

  1. The extracted tree is genuinely bigger than the free space. Check the archive's expected uncompressed size (most formats report it in their metadata, and peel logs it on the progress UI's first line). If that exceeds free space, more disk is required: the sliding window only bounds the compressed side.

  2. The part-file's lookahead window grew faster than the decoder consumed it. Lower --max-disk-buffer (default 1 GiB) so the scheduler back-pressures sooner:

    peel <URL> --max-disk-buffer 128MiB -o ./out/
    

    Then confirm hole-punching is working (see the next section).

"Hole-punching seems disabled / part-file is huge"

Check the part-file's physical size (du -h) versus its logical size (ls -la):

$ ls -la out.peel.part out.peel.ckpt
-rw-r--r--  ...  10737418240 ... out.peel.part   # 10 GiB logical
$ du -h out.peel.part
182M    out.peel.part                           # 182 MiB physical (healthy)

If du is close to ls, hole-punching is not trimming. Possible causes:

  • -k/--keep-archive is set. The puncher is intentionally disabled. Remove -k if archive preservation is not required.

  • --no-extract is set. Nothing decodes, so nothing punches. Expected for --no-extract: the bytes are kept verbatim.

  • The filesystem does not support punch-hole. Some unusual mounts and old kernels reject it. peel logs a warn! at startup when the probe fails:

    WARN  filesystem rejected MADV_REMOVE probe, falling back to fallocate(PUNCH_HOLE)
    WARN  filesystem rejected fallocate(PUNCH_HOLE) probe, source bytes will not be released
    

    Move the workdir to a filesystem that supports it (--workdir /var/tmp/peel), or accept the larger transient footprint.

"io_uring fallback warning"

On Linux, one of these messages may appear:

WARN  io_uring_setup failed (errno=1 EPERM), falling back to blocking sockets
WARN  io_uring not available (kernel < 5.6), falling back to blocking sockets
WARN  RLIMIT_MEMLOCK too low for io_uring (need at least N KiB), falling back to blocking sockets

These are informational, not errors. peel falls back to the blocking backend and continues. The fallback path is the same code every non-Linux build uses; results are correct either way.

To force io_uring and fail-fast when it is not available:

peel <URL> --io-backend uring -o ./out/

Common causes:

  • Seccomp profile blocks the syscalls. cri-o's default profile is the most common case under Kubernetes. Add io_uring_* to the allowed syscalls or accept the fallback.
  • Kernel too old. Minimum 5.6 for the SQEs peel uses. In practice 5.10+ is more reliable.
  • RLIMIT_MEMLOCK too low. Container default may be 16 KiB, while io_uring rings need a few MiB. Raise the limit (ulimit -l unlimited in the container spec) or accept the fallback.

"Wrong digest at completion"

error: digest mismatch
  expected: ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad
  got:      e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

The streamed bytes did not hash to the value asserted by --sha256. Possibilities:

  1. The source moved or was re-uploaded. Re-download a small sample (curl --range 0-1023 $URL | sha256sum) and compare against what the publisher currently advertises.
  2. Wrong hash supplied. Double-check the hash source.
  3. A --mirror is serving subtly different bytes. Remove mirrors one at a time to identify the culprit. peel drops misbehaving mirrors at the HEAD validation step, but some forms of subtle corruption only show up post-decoder.

"Source changed during run"

error: source changed during run
  chunk 1247 fingerprint mismatch: stored=…, refetched=…

The CRC32C fingerprint of a chunk does not agree across fetches. The source bytes changed between when one worker fetched the chunk and when another did. Causes:

  • CDN-edge cache drift. Common with mirror infrastructure that is mid-rollout. Wait for propagation, then re-run.
  • Origin re-uploaded the file. Check the upstream publishing timeline.
  • Network corruption. Rare with TLS, but observed on some middlebox-heavy paths. Repeat the run; the second attempt usually succeeds.

Delete the .peel.ckpt to start fresh from the part-file's bytes (which peel re-verifies chunk-by-chunk), or delete both sidecars to start completely from scratch.

"ETag mismatch on resume"

error: source identity changed since last run
  ETag at startup: "8b1a9953c4611296a827abf8c47804d7"
  ETag now:        "65a8e27d8879283831b664bd8b7f0ad4"

The source's ETag (or Last-Modified) changed between the start of the run and the resume. peel aborts rather than silently mixing bytes from two different versions of the file.

Two fixes:

  1. Delete the sidecars and start from scratch. The bytes already on disk belong to the previous version and are not useful.
  2. Pass --sha256 of the new version. With --sha256 set, the hash is the source of truth, and agreeing mirrors are trusted regardless of ETag drift.

"Multi-volume probe returned 404s"

warn: multi-volume probe: backup.part0005.rar returned 404, stopping at 4 volumes

This is normal: auto-discovery stops at the first missing volume. The reported count is what peel will fetch.

If more volumes are expected than the probe found:

  • The volumes may be on a different host or path.
  • The numbering may have a gap.
  • The volumes may use a different convention than the seed implies.

Use the explicit positional list or an @manifest.txt file instead.

"Wrong format detected"

error: format mismatch: URL suffix says .tar.zst, magic bytes are 0x1f 0x8b (gzip)
       pass --force-format-from-magic to trust magic, or --format <NAME> to pin

The URL suffix and the magic bytes disagree. Three options:

  1. Trust the magic when the file is known to match its bytes: --force-format-from-magic.
  2. Pin the decoder when the expected format is known: --format zstd (or whichever).
  3. Investigate the source. The file may have been re-encoded without renaming, or the URL is genuinely serving the wrong file.

"Permission denied" writing the output

The output's parent directory is not writable for the user running peel. The error message names the path:

error: cannot create output directory ./out/: Permission denied (os error 13)

peel does not elevate privileges. Run as a user with write permission on the output path, or use --workdir to relocate only the sidecars to a writable location while writing the final extracted tree to a location the user owns.

"Output file already exists" (or seems to)

peel overwrites the extracted output:

  • For tree-shaped outputs (tar, zip, 7z, rar), existing files at paths matching an archive entry are overwritten. Existing files at paths not in the archive are left alone.
  • For stream-shaped outputs (raw .zst, .xz, .lz4, .gz), the output file is overwritten unconditionally.

For a non-destructive run, point -o at a fresh directory.

"I want to interrupt and resume later"

Press Ctrl-C (or send SIGTERM). peel traps the signal, flushes the in-flight checkpoint, and exits with code 130 (SIGINT) or 143 (SIGTERM). The sidecars stay on disk. Re-run the exact same command to resume.

kill -9 (SIGKILL) is also safe. peel is designed so that even an ungraceful kill leaves the part-file's bytes and the last checkpoint in a consistent state, and the next run reconciles.

"Where do the logs go?"

stderr. The progress UI block goes to stderr as well, redrawn in place on a TTY. To capture:

peel <URL> -o ./out/ 2>peel.log                    # only log
peel <URL> -o ./out/ 2> >(tee peel.log >&2)        # log and show
RUST_LOG=debug peel <URL> -o ./out/ 2>peel.log     # verbose

"Live progress UI shows wrong percentage"

The percentage is streamed_bytes / Content-Length. Two pitfalls:

  • For multi-part URLs, the denominator is the sum of all parts' Content-Length values (accurate).
  • For a server that does not return Content-Length (rare, mostly badly-configured proxies), peel falls back to a chunk-count progress estimate. The percentage will be approximate.

Getting better diagnostics

Always run with RUST_LOG=info (or RUST_LOG=debug) when filing a bug report:

RUST_LOG=debug peel <URL> -o ./out/ 2>peel-debug.log

The first few lines list the selected backends, the discovered volumes and mirrors, and the format detection result. Misbehaviour typically shows up there.