benbjohnson/litestream · error

failed to scan directory %s: %w

Error message

failed to scan directory %s: %w

What it means

After expanding the directory path, NewDBsFromDirectoryConfig calls FindSQLiteDatabases to enumerate databases matching the pattern. If that filesystem scan fails (I/O error rather than merely no matches), the error is wrapped with the directory path to identify what could not be scanned.

Source

Thrown at cmd/litestream/main.go:835

		return nil, fmt.Errorf("directory path is required for directory replication")
	}

	if dbc.Pattern == "" {
		return nil, fmt.Errorf("pattern is required for directory replication")
	}
	if dbc.MetaPath != nil && dbc.MetaDir != nil {
		return nil, fmt.Errorf("cannot specify both 'meta-path' and 'meta-dir'")
	}

	dirPath, err := expand(dbc.Dir)
	if err != nil {
		return nil, err
	}

	// Find all SQLite databases in the directory
	dbPaths, err := FindSQLiteDatabases(dirPath, dbc.Pattern, dbc.Recursive)
	if err != nil {
		return nil, fmt.Errorf("failed to scan directory %s: %w", dirPath, err)
	}

	if len(dbPaths) == 0 && !dbc.Watch {
		return nil, fmt.Errorf("no SQLite databases found in directory %s with pattern %s", dirPath, dbc.Pattern)
	}

	// Create DB instances for each found database
	var dbs []*litestream.DB
	metaPaths := make(map[string]string)

	for _, dbPath := range dbPaths {
		db, err := newDBFromDirectoryEntry(dbc, dirPath, dbPath)
		if err != nil {
			return nil, fmt.Errorf("failed to create DB for %s: %w", dbPath, err)
		}

		// Validate unique meta-path to prevent replication state corruption
		if mp := db.MetaPath(); mp != "" {

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Check that the configured directory exists and is readable by the litestream process at startup.
  2. Read the wrapped inner error to identify the failing path/file and fix the filesystem or permission problem.
  3. If the directory is on a network mount, add a startup dependency (systemd After=/Requires=) so the mount is ready before litestream starts.
  4. Exclude problematic subdirectories (narrow the pattern) if a specific path is unreadable.

Example fix

// before
dirPath, _ := expand(dbc.Dir) // mount may not be ready

// after
if _, err := os.Stat(dirPath); err != nil {
    return nil, fmt.Errorf("directory %s not ready: %w", dirPath, err) // surface before scanning
}
Defensive patterns

Strategy: validation

Validate before calling

if _, err := os.Stat(dirPath); err != nil {
    return fmt.Errorf("replication dir %s unavailable: %w", dirPath, err)
}

Try / catch

if err := runLitestream(ctx); err != nil {
    var scanErr *fs.PathError
    if errors.As(err, &scanErr) {
        log.Warnf("directory scan failed, retrying after mount check: %v", err)
    }
}

Prevention

When it happens

Trigger: FindSQLiteDatabases(dirPath, pattern, recursive) returns a non-nil error — e.g. the directory disappeared between expand and scan, an I/O error occurred while reading a subdirectory, or permission errors surfaced as errors from the walk.

Common situations: Directory is a mount that is not yet mounted at startup; network filesystem (NFS/EFS) hiccup; symlink loop or unreadable subdirectory under a recursive scan; container volume not attached yet.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/eb12b51284b12915. Report an issue: GitHub.