ory/hydra · error

driver %T does not support online restore

Error message

driver %T does not support online restore

What it means

Inside conn.Raw, the code type-asserts the unwrapped driver connection to an interface with NewRestore(srcUri string) (*moderncsqlite.Backup, error). If the concrete driver does not implement online backup, errors.Errorf produces 'driver %T does not support online restore', reporting the actual driver type.

Source

Thrown at oryx/popx/migrator.go:103

// pop wraps the modernc.org/sqlite driver with otelsql for tracing; we
// unwrap via the otelsql Raw() accessor before reaching the driver-specific
// NewRestore method on the underlying *sqlite.conn.
func restoreSQLiteOnline(ctx context.Context, db *sql.DB, srcPath string) error {
	conn, err := db.Conn(ctx)
	if err != nil {
		return errors.Wrap(err, "failed to acquire sql.Conn for restore")
	}
	defer func() { _ = conn.Close() }()

	return conn.Raw(func(driverConn any) error {
		if w, ok := driverConn.(interface{ Raw() driver.Conn }); ok {
			driverConn = w.Raw()
		}
		restorer, ok := driverConn.(interface {
			NewRestore(srcUri string) (*moderncsqlite.Backup, error)
		})
		if !ok {
			return errors.Errorf("driver %T does not support online restore", driverConn)
		}
		// See: https://sqlite.org/backup.html .
		backup, err := restorer.NewRestore(srcPath)
		if err != nil {
			return errors.Wrap(err, "NewRestore failed")
		}
		// Step(-1) copies all remaining pages in one call.
		for {
			more, stepErr := backup.Step(-1)
			if stepErr != nil {
				_ = backup.Finish()
				return errors.Wrap(stepErr, "backup.Step failed")
			}
			if !more {
				break
			}
		}
		return errors.Wrap(backup.Finish(), "backup.Finish failed")

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Use the supported modernc.org/sqlite driver (via the otelsql wrapper the library configures) for online restore
  2. Remove or adjust extra driver wrappers so the Raw() unwrap reaches a *sqlite.conn
  3. Fall back to file-copy restore instead of the online backup API
  4. Guard the restore with a capability check before invoking it

Example fix

// before
import _ "github.com/mattn/go-sqlite3"
// after
import _ "modernc.org/sqlite"
Defensive patterns

Strategy: fallback

Type guard

func supportsOnlineRestore(driverConn any) bool {
  if w, ok := driverConn.(interface{ Raw() driver.Conn }); ok {
    driverConn = w.Raw()
  }
  _, ok := driverConn.(interface {
    NewRestore(srcUri string) (*moderncsqlite.Backup, error)
  })
  return ok
}

Try / catch

if err := restoreSQLiteOnline(ctx, db, srcPath); err != nil {
  if strings.Contains(err.Error(), "does not support online restore") {
    // fall back to file-based restore
    err = fileCopyRestore(db, srcPath)
  }
  return err
}

Prevention

When it happens

Trigger: Running the SQLite online-restore path while connected via a driver whose connection is neither *sqlite.conn nor otherwise implementing NewRestore — e.g. a pure-Go alternative driver, a wrapped driver not unwrappable via Raw(), or mattn/go-sqlite3.

Common situations: Swapping the SQLite driver (cgo vs modernc), stacking extra driver wrappers that break the Raw() unwrapping chain, running migrations on a non-SQLite database that mistakenly routes into this code path.

Related errors


AI-assisted analysis of ory/hydra@4174065ffb (2026-09-03). Data as JSON: /api/errors/3c4c2a4e3ff1b176. Report an issue: GitHub.