Investigation: List (snapshot) returned error

Hi guys! Could you help me to investigate what happened here?

I connected a flash drive with restic backup, I ran restic snapshots -v and it failed with the error:

% restic_cmd snapshots -v
repository 1bcaea8b opened (version 2, compression level auto)
found 1 old cache directories in
/Users/nett/Library/Caches/restic, run 'restic cache --cleanup to remove them
List (snapshot) returned error, retrying after 1.48393821s: open /Volumes/ext/backup/snapshots: invalid argument 
List (snapshot) returned error, retrying after 2.6367773s: open /Volumes/ext/backup/snapshots: invalid argument 
List (snapshot) returned error, retrying after 3.912962166s: open /Volumes/ext/backup/snapshots: invalid argument

I tried ls and it seems the fs (ExFat) was corrupted:

% ls /Volumes/ext/backup/
config data index keys locks snapshots

% ls /Volumes/ext/backup/snapshots
ls: /Volumes/ext/backup/snapshots: Invalid argument

I used Mac OS Disk Utility to check and fix the fs:

Running First Aid on “ext” (disk4s2)

Checking file system and repairing if necessary and if possible.
Volume was successfully unmounted.
Performing fsck_exfat -y -x /dev/rdisk4s2
Checking volume.
Checking main boot region.
Checking system files.
Volume name is ext.
Checking upper case translation table.
Checking file system hierarchy.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found directory entry beyond end of directory in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
/backup/snapshots starts with an invalid cluster (159496416).
Found incomplete directory entry set in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
/backup/snapshots starts with an invalid cluster (1714698593).
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
/backup/snapshots starts with an invalid cluster (1667654449).
Found incomplete directory entry set in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
/backup/snapshots starts with an invalid cluster (876033893).
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found directory entry beyond end of directory in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
/backup/snapshots starts with an invalid cluster (811873841).
Found incomplete directory entry set in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
/backup/snapshots starts with an invalid cluster (811940148).
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected critical primary directory entry in /backup/snapshots.
/backup/snapshots starts with an invalid cluster (1630613812).
Found incomplete directory entry set in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found directory entry beyond end of directory in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found directory entry beyond end of directory in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
/backup/snapshots starts with an invalid cluster (159496416).
Found incomplete directory entry set in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected critical primary directory entry in /backup/snapshots.
/backup/snapshots starts with an invalid cluster (671419399).
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
/backup/snapshots starts with an invalid cluster (1647600178).
Found incomplete directory entry set in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
/backup/snapshots starts with an invalid cluster (1650864740).
Found incomplete directory entry set in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
/backup/snapshots starts with an invalid cluster (839193863).
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found incomplete directory entry set in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected critical primary directory entry in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
Found directory entry beyond end of directory in /backup/snapshots.
/backup/snapshots starts with an invalid cluster (962880312).
Found incomplete directory entry set in /backup/snapshots.
Found directory entry beyond end of directory in /backup/snapshots.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
Found an unexpected secondary directory entry in %1$@.
Checking active bitmap.
The bitmap needs to be repaired.
Rechecking main boot region.
Rechecking alternate boot region.
The volume ext was repaired successfully.
File system check exit code is 0.
Restoring the original state found as mounted.

Operation successful.

Followed by restic check --read-data that says that I need restic prune:

% ./check.sh ext.env 
using temporary cache in /var/folders/sp/9l83qcws3j512t_9nh1k9wnm0000gn/T/restic-check-cache-1355820747
create exclusive lock for repository
repository 1bcaea8b opened (version 2, compression level auto)
created new cache in /var/folders/sp/9l83qcws3j512t_9nh1k9wnm0000gn/T/restic-check-cache-1355820747
load indexes
[0:00] 100.00%  75 / 75 index files loaded
check all packs
9 additional files were found in the repo, which likely contain duplicate data.
This is non-critical, you can run `restic prune` to correct this.
check snapshots, trees and blobs
[0:01] 100.00%  75 / 75 snapshots
read all data
[0:47] 100.00%  484 / 484 packs
no errors were found

Then I ran restic backup followed by restic diff to see what’s changed between the backup and the local filesystem. Both commands have completed successfully and didn’t show any corrupted files.

So it seems that I’m clear to run restic prune, which I haven’t run yet.

I want to find a plausible explanation of what’s happened. The last restic backup was today, and it was followed by restic check –read-data, both have completed successfully. The USB drive was safely unmounted and ejected after that. I thought that this leaves the fs and the backup in a good condition. This is the first fs corruption I see in years. The only process writing to the drive was restic

% restic version
restic 0.18.1 compiled with go1.25.1 on darwin/arm64

Upd: I tried restic backup -f followed by restic diff. Both commands have completed successfully and didn’t show any modified files. It seems the -f key didn’t consume extra storage:

snapshots for (paths [/Users/nett/Documents/books]):
ID        Time                 Host        Tags        Paths                        Size
-----------------------------------------------------------------------------------------------
c99ac0ff  2026-02-04 12:26:00  air.local               /Users/nett/Documents/books  999.345 MiB
7ea11fe3  2026-02-04 12:32:36  air.local               /Users/nett/Documents/books  999.345 MiB
9dcc729d  2026-02-04 12:35:56  air.local               /Users/nett/Documents/books  999.345 MiB
a62a079e  2026-02-05 03:02:43  air.local               /Users/nett/Documents/books  999.345 MiB
33cc5545  2026-02-05 04:09:22  air.local               /Users/nett/Documents/books  999.345 MiB
-----------------------------------------------------------------------------------------------
5 snapshots

so can I use restic backup -f for daily backups? The help says: force re-reading the source files/directories (overrides the “parent” flag). Yes, of course I want to re-read the source files every time I do backup. Why it’s not a default?

Upd2: I ran restic prune

% restic_cmd prune    
repository 1bcaea8b opened (version 2, compression level auto)
found 1 old cache directories in /Users/nett/Library/Caches/restic, run `restic cache --cleanup` to remove them
loading indexes...
[0:00] 100.00%  126 / 126 index files loaded
loading all snapshots...
finding data that is still in use for 126 snapshots
[0:01] 100.00%  126 / 126 snapshots
searching used packs...
collecting packs for deletion and repacking
[0:00] 100.00%  538 / 538 packs processed

to repack:          1361 blobs / 1.113 MiB
this removes:          0 blobs / 0 B
to delete:            28 blobs / 147.236 MiB
total prune:          28 blobs / 147.236 MiB
remaining:        177960 blobs / 6.541 GiB
unused size after prune: 0 B (0.00% of remaining size)

deleting unreferenced packs
[0:00] 100.00%  9 / 9 files deleted
repacking packs
[0:00] 100.00%  118 / 118 packs repacked
rebuilding index
[0:00] 100.00%  127 / 127 indexes processed
[0:00] 100.00%  124 / 124 old indexes deleted
removing 132 old packs
[0:00] 100.00%  132 / 132 files deleted
done