benbjohnson/litestream · error

driver does not implement FileControl

Error message

driver does not implement FileControl

What it means

Litestream requires the underlying SQLite driver to expose the FileControl interface so it can issue the PERSIST_WAL file control, which keeps the WAL file from being deleted when database connections close. The code reaches into the raw driver connection via database/sql's Conn.Raw and asserts it to sqlite.FileControl; if the registered 'sqlite' driver's connection type does not implement that interface, the assertion fails and this error is returned. It effectively means the litestream binary was linked against an incompatible (usually too old) version of modernc.org/sqlite.

Source

Thrown at db.go:1011

				"duration", time.Since(startTime))
			return fmt.Errorf("after %d attempts: %w", attempt, ErrShutdownInterrupted)
		}
	}
}

// setPersistWAL sets the PERSIST_WAL file control on the database connection.
// This prevents SQLite from removing the WAL file when connections close.
func (db *DB) setPersistWAL(ctx context.Context) error {
	conn, err := db.db.Conn(ctx)
	if err != nil {
		return fmt.Errorf("get connection: %w", err)
	}
	defer conn.Close()

	return conn.Raw(func(driverConn interface{}) error {
		fc, ok := driverConn.(sqlite.FileControl)
		if !ok {
			return fmt.Errorf("driver does not implement FileControl")
		}

		_, err := fc.FileControlPersistWAL("main", 1)
		if err != nil {
			return fmt.Errorf("FileControlPersistWAL: %w", err)
		}

		return nil
	})
}

// init initializes the connection to the database.
// Skipped if already initialized or if the database file does not exist.
func (db *DB) init(ctx context.Context) (err error) {
	// Exit if already initialized.
	if db.db != nil {
		return nil
	}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Run `go get modernc.org/sqlite@latest` (or at least a version that implements FileControl) and rebuild: `go build -o bin/litestream ./cmd/litestream`
  2. Run `go mod tidy` and inspect `go mod graph | grep modernc.org/sqlite` to find a dependency forcing an older version, then upgrade or add an explicit require
  3. If vendoring, refresh the vendor directory with `go mod vendor` after upgrading so the new driver code is present
  4. Verify no alternative driver is registered under the name 'sqlite' that shadows modernc.org/sqlite's driver

Example fix

// before (go.mod)
require modernc.org/sqlite v1.18.0 // lacks FileControl
// after
require modernc.org/sqlite v1.29.10 // implements sqlite.FileControl
Defensive patterns

Strategy: type-guard

Validate before calling

import "modernc.org/sqlite"
var _ sqlite.FileControl = ??? // at build time, verify driver version:
// go list -m modernc.org/sqlite  → ensure >= version with FileControl
typFileControl := func(dc any) (sqlite.FileControl, bool) {
    fc, ok := dc.(sqlite.FileControl)
    return fc, ok
}

Type guard

func asFileControl(driverConn any) (sqlite.FileControl, bool) {
    fc, ok := driverConn.(sqlite.FileControl)
    return fc, ok
}

Try / catch

err := dbConn.Raw(func(dc any) error {
    fc, ok := dc.(sqlite.FileControl)
    if !ok {
        return fmt.Errorf("driver does not implement FileControl")
    }
    _, err := fc.FileControlPersistWAL("main", 1)
    return err
})
if err != nil { log.Fatal(err) }

Prevention

When it happens

Trigger: Calling DB.init -> db.setPersistWAL: the connection obtained from db.db.Conn(ctx) returns a raw driver connection whose concrete type does not satisfy sqlite.FileControl, so the type assertion driverConn.(sqlite.FileControl) in conn.Raw fails. This happens when the module graph resolves modernc.org/sqlite to a version predating FileControl/FileControlPersistWAL support, or when a different driver named 'sqlite' was registered and wins driver lookup.

Common situations: Building litestream from source with a stale go.mod pin or vendor directory containing an old modernc.org/sqlite; a downstream application importing litestream as a library that also pins an older modernc.org/sqlite, causing a downgrade via MVS; vendoring tools pruning or replacing the driver; using a fork or alternative pure-Go 'sqlite' driver that lacks FileControl.

Understand the failure class

Background: "NotImplementedError: Subclasses should override this method" / "must be implemented" — abstract method errors explained — this error's family across 40 libraries.

Related errors


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