benbjohnson/litestream · error

database config #%d: 'watch' can only be enabled with a dire

Error message

database config #%d: 'watch' can only be enabled with a directory

What it means

Validate() in cmd/litestream/main.go rejects a database config entry where 'watch: true' is set but no 'dir' is configured. Watch-mode file monitoring only works for directory-based replication, where Litestream scans a directory for SQLite databases; for a single-database 'path' config it is meaningless. The check fires per database entry during config parsing.

Source

Thrown at cmd/litestream/main.go:518

		}

		// Reject the same database path listed twice. The metadata directory is
		// derived from the path, so two entries would share it and race on the
		// same LTX temp files.
		if db.Path != "" {
			key := filepath.Clean(db.Path)
			if first, ok := seenPaths[key]; ok {
				return fmt.Errorf("database config #%d: duplicate path %q (already used by database config #%d); each database can be listed only once", idx+1, db.Path, first)
			}
			seenPaths[key] = idx + 1
		}

		// When using dir, pattern must be specified
		if db.Dir != "" && db.Pattern == "" {
			return fmt.Errorf("database config #%d: 'pattern' is required when using 'dir'", idx+1)
		}
		if db.Watch && db.Dir == "" {
			return fmt.Errorf("database config #%d: 'watch' can only be enabled with a directory", idx+1)
		}
		if db.MetaDir != nil && db.Dir == "" {
			return fmt.Errorf("database config #%d: 'meta-dir' can only be used with a directory", idx+1)
		}
		if db.MetaPath != nil && db.MetaDir != nil {
			return fmt.Errorf("database config #%d: cannot specify both 'meta-path' and 'meta-dir'", idx+1)
		}

		// Use path or dir for identifying the config in error messages
		dbIdentifier := db.Path
		if dbIdentifier == "" {
			dbIdentifier = db.Dir
		}

		if db.Snapshot.Interval != nil && *db.Snapshot.Interval <= 0 {
			return &ConfigValidationError{
				Err:   ErrInvalidSnapshotInterval,
				Field: fmt.Sprintf("dbs[%s].snapshot.interval", dbIdentifier),

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Remove `watch: true` from the single-database config entry, or
  2. Convert the entry to directory-based replication by setting `dir:` (and the required `pattern:`) instead of `path:`, keeping `watch: true`.
  3. If periodic monitoring is desired for a single DB, rely on the default monitor interval; watch is not supported for path-based configs.

Example fix

# before
databases:
  - path: /var/db/app.db
    watch: true
# after (option A: drop watch)
databases:
  - path: /var/db/app.db
# after (option B: use dir)
databases:
  - dir: /var/db
    pattern: "*.db"
    watch: true
Defensive patterns

Strategy: validation

Validate before calling

for i, db := range cfg.Databases {
    if db.Watch && db.Dir == "" {
        return fmt.Errorf("db #%d: 'watch' requires 'dir'", i+1)
    }
}

Prevention

When it happens

Trigger: Running `litestream` (or ParseConfig -> Validate) with a YAML/JSON config where a databases[] entry has `watch: true` but only `path:` set (no `dir:`).

Common situations: User copies watch settings from a directory-replication example into a single-DB config; user misunderstands 'watch' as a general polling option and enables it on a path-based database entry.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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