diff --git a/CHANGELOG.md b/CHANGELOG.md
index 98d5d67..34f51d0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,7 +21,8 @@ Bugfix release.
- Backups: the runs table scrolls horizontally on narrow screens instead of running off the page.
- `occ backup:verify --deep`: a blob that could not be read (network or storage error) was reported
as "corrupted or tampered". Reads are now retried, and a read that keeps failing is reported
- separately as unreadable, with the error; only a blob that was read but fails decryption or its
+ separately as unreadable, with the error (run the check again; if the same blobs stay unreadable,
+ their pack may be damaged or truncated). Only a blob that was read but fails decryption or its
hash check counts as corrupted. Found on a Hetzner Storage Box over WebDAV, where the blob
turned out to be intact.
diff --git a/README.md b/README.md
index 43619b9..d7691f3 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,9 @@ Contributions welcome via PR — add what you ran into with your own provider.
- **Hetzner Storage Box over WebDAV and SMB (sub-accounts, 2026-10-07):** both work end to end
(backup, incremental run, restore of a folder, verify), tested from outside the Hetzner network
- with about 66 MiB of data. WebDAV: host `https://uXXXXX-subN.your-storagebox.de`, and the `root`
+ with about 66 MiB of data. In the Hetzner Console, enable WebDAV or SMB support for the
+ sub-account, and "External reachability" when your server is not in the Hetzner network.
+ WebDAV: host `https://uXXXXX-subN.your-storagebox.de`, and the `root`
must be a folder that already exists (or empty): WebDAV does not create missing parent folders,
so a root that doesn't exist yet fails `backup:target:add` with `Sabre\HTTP\ClientHttpException:
Conflict`. SMB: share = the sub-account name, port 445 must be reachable from your server (it was
diff --git a/lib/Command/Verify.php b/lib/Command/Verify.php
index 148f476..37997ab 100644
--- a/lib/Command/Verify.php
+++ b/lib/Command/Verify.php
@@ -67,8 +67,9 @@ HELP);
$output->writeln('' . count($result['failed']) . ' blob(s) failed verification (corrupted or tampered): ' . implode(', ', $result['failed']));
}
if ($result['unreadable'] !== []) {
- $output->writeln('' . count($result['unreadable']) . ' blob(s) could not be read from the location (storage or network error, '
- . 'not a sign of corruption; run the check again): ' . implode(', ', $result['unreadable']));
+ $output->writeln('' . count($result['unreadable']) . ' blob(s) could not be read from the location (often a temporary storage '
+ . 'or network error: run the check again; if the same blobs stay unreadable, their pack may be damaged or truncated): '
+ . implode(', ', $result['unreadable']));
$output->writeln('Last read error: ' . $result['readError']);
}
if ($result['missing'] === [] && $result['failed'] === [] && $result['unreadable'] === []) {
diff --git a/lib/Repository/Repository.php b/lib/Repository/Repository.php
index be0b35d..f45a4e2 100644
--- a/lib/Repository/Repository.php
+++ b/lib/Repository/Repository.php
@@ -831,10 +831,10 @@ final class Repository {
if (!$deep) {
return;
}
- // Reading and checking are kept apart: a read that fails (network, storage) says
- // nothing about the stored data, so it is retried and reported as unreadable,
- // never as corrupted. Only a blob that was read but does not decrypt or match
- // its id counts as failed.
+ // Reading and checking are kept apart: a read that fails is usually a network or
+ // storage error, so it is retried and reported as unreadable (a pack that stays
+ // unreadable may still be truncated). Only a blob that was read but does not
+ // decrypt or match its id counts as failed: that is proof of damage.
[, $offset, $length] = $this->index->get($id);
$encrypted = null;
for ($attempt = 1; $attempt <= self::VERIFY_READ_ATTEMPTS && $encrypted === null; $attempt++) {