restic/restic · error
unexpected file type %v at %q
Error message
unexpected file type %v at %q
What it means
openFile in the restorer opens an existing destination path with O_WRONLY|O_NOFOLLOW and 0600, then Stats it and refuses anything that is not a regular file; this error names the file type and path. FIFOs, device nodes, sockets and similar special files cannot receive restored file content. (Symlinks fail earlier at open with ELOOP because of O_NOFOLLOW; directories fail with EISDIR.) It fires during restore when the target path for a regular file is occupied by a special file.
Source
Thrown at internal/restorer/fileswriter.go:77
buckets: buckets,
allowRecursiveDelete: allowRecursiveDelete,
cache: cache,
}
}
func openFile(path string) (*os.File, error) {
f, err := fs.OpenFile(path, fs.O_WRONLY|fs.O_NOFOLLOW, 0600)
if err != nil {
return nil, err
}
fi, err := f.Stat()
if err != nil {
_ = f.Close()
return nil, err
}
if !fi.Mode().IsRegular() {
_ = f.Close()
return nil, fmt.Errorf("unexpected file type %v at %q", fi.Mode().Type(), path)
}
return f, nil
}
func createFile(path string, createSize int64, sparse bool, allowRecursiveDelete bool) (*os.File, error) {
f, err := fs.OpenFile(path, fs.O_CREATE|fs.O_WRONLY|fs.O_NOFOLLOW, 0600)
if err != nil && fs.IsAccessDenied(err) {
// If file is readonly, clear the readonly flag by resetting the
// permissions of the file and try again
// as the metadata will be set again in the second pass and the
// readonly flag will be applied again if needed.
if err = fs.ResetPermissions(path); err != nil {
return nil, err
}
if f, err = fs.OpenFile(path, fs.O_WRONLY|fs.O_NOFOLLOW, 0600); err != nil {
return nil, err
}
} else if err != nil && (errors.Is(err, syscall.ELOOP) || errors.Is(err, syscall.EISDIR)) {View on GitHub (pinned to a80be1478a)
Solutions
- Inspect the path: ls -l / stat on the reported target to see the special file type
- Remove or relocate the special file, then restore again
- Restore into a fresh, empty directory instead of over an existing tree
Example fix
# before restic restore latest --target /srv/restore # after rm -f /srv/restore/var/run/notify.fifo # the reported special file restic restore latest --target /srv/restore
Defensive patterns
Strategy: validation
Validate before calling
if fi, err := fs.Lstat(path); err == nil && !fi.Mode().IsRegular() {
return fmt.Errorf("refusing to restore over %s at %s — remove or move it first", fi.Mode().Type(), path)
} Type guard
func isRegularFilePath(path string) bool {
fi, err := os.Lstat(path)
return err == nil && fi.Mode().IsRegular()
} Prevention
- Restore into a fresh target directory when possible
- Pre-clean destinations built from container/VM images that contain device nodes or FIFOs
- On Windows, avoid restoring files named after reserved devices (NUL, CON, COM1)
When it happens
Trigger: Restoring into a destination where a previous process created a named pipe, unix socket or device node at a path that is a regular file in the snapshot; on Windows, reserved device names (NUL, CON, COM1) used as filenames; container/VM images whose extraction created device nodes prematurely.
Common situations: Restoring over an unpacked rootfs image that contains /dev-like nodes; leftover chroot/container build trees; intentionally booby-trapped directories with FIFOs at file paths.
Related errors
- failed to remove stale item: %w
- failed to restore timestamp of %q: %w
- failed to check for directory: %w
- cannot create target directory: %w
- failed to encoded extended attributes: %w
AI-assisted analysis of restic/restic@a80be1478a (2026-08-15).
Data as JSON: /api/errors/76d306a721c315a4.
Report an issue: GitHub.