restic/restic · error

invalid filename specified

Error message

invalid filename specified

What it means

NewReader builds a virtual FS that exposes exactly one file at the given name. The name is cleaned first; if it cleans to "/" there is no filename component to place the reader at, so the constructor rejects it with "invalid filename specified".

Source

Thrown at internal/fs/fs_reader.go:49

	fi             *ExtendedFileInfo
	rc             io.ReadCloser
	allowEmptyFile bool

	children []string
}

// statically ensure that Local implements FS.
var _ FS = &reader{}

// NewReader returns a new FS which provides a directory with a single file. When
// this file is opened for reading, the reader is passed through. The file can
// be opened once, all subsequent open calls return syscall.EIO. For Lstat(),
// the provided FileInfo is returned.
func NewReader(name string, r io.ReadCloser, opts ReaderOptions) (FS, error) {
	items := make(map[string]readerItem)
	name = readerCleanPath(name)
	if name == "/" {
		return nil, fmt.Errorf("invalid filename specified")
	}

	isFile := true
	for {
		if isFile {
			fi := &ExtendedFileInfo{
				Name:    path.Base(name),
				Mode:    opts.Mode,
				ModTime: opts.ModTime,
				Size:    opts.Size,
			}
			items[name] = readerItem{
				open:           &sync.Once{},
				fi:             fi,
				rc:             r,
				allowEmptyFile: opts.AllowEmptyFile,
			}
			isFile = false

View on GitHub (pinned to a80be1478a)

Solutions

  1. Pass a concrete filename such as "stdin" (restic's own convention is /stdin)
  2. Guard upstream code that computes the name so it never reduces to the root

Example fix

// before
fs, err := fs.NewReader(filepath.Clean(userPath), r, opts) // userPath == "/"

// after
name := path.Clean(userPath)
if name == "/" || name == "" {
	name = "stdin"
}
fs, err := fs.NewReader(name, r, opts)
Defensive patterns

Strategy: validation

Validate before calling

name = path.Clean(name)
if name == "/" || name == "" {
	return errors.New("reader filename must not be the filesystem root")
}

Prevention

When it happens

Trigger: Calling NewReader("/", r, opts) or with a name that readerCleanPath reduces to the root (empty string, "/", "//").

Common situations: Defaulting a filename variable to "/" or "" when the caller meant something like "stdin"; path-joining bugs that strip the last component.

Related errors


AI-assisted analysis of restic/restic@a80be1478a (2026-08-15). Data as JSON: /api/errors/2f4a265112fbbfb3. Report an issue: GitHub.