ory/hydra · error

failed to acquire sql.Conn for restore

Error message

failed to acquire sql.Conn for restore

What it means

restoreSQLiteOnline performs an online SQLite backup by first acquiring a dedicated *sql.Conn from the pool. If db.Conn(ctx) fails (context canceled/expired or pool acquisition failure), the error is wrapped as 'failed to acquire sql.Conn for restore'.

Source

Thrown at oryx/popx/migrator.go:91

	dir := mb.sqliteTemplateCacheDir
	if dir == "" {
		dir = os.TempDir()
	}
	return filepath.Join(dir, "ory-popx-sqlite-template-"+hex.EncodeToString(h.Sum(nil))+".sqlite")
}

// restoreSQLiteOnline streams the contents of srcPath into the database
// represented by db using SQLite's online backup API. The destination
// connection stays open throughout, so the *sql.DB and any pop.Connection
// holding it remain valid.
//
// 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")
		}

View on GitHub (pinned to 4174065ffb)

Solutions

  1. Check the wrapped cause: if context deadline, increase the timeout or use context.Background() for migrations
  2. Ensure the *sql.DB is open and not closed before UpTo runs
  3. Avoid holding all pool connections concurrently with the restore
  4. Retry the migration after freeing connections

Example fix

// before
err := mb.UpTo(ctx, -1) // ctx already near deadline
// after
restoreCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
err := mb.UpTo(restoreCtx, -1)
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure context is healthy and DB is open before migrating
if err := ctx.Err(); err != nil { return err }
if err := db.PingContext(ctx); err != nil { return err }

Try / catch

if err := mb.UpTo(ctx, -1); err != nil {
  if errors.Is(err, context.DeadlineExceeded) {
    // retry with a fresh, longer-lived context
  }
  if strings.Contains(err.Error(), "failed to acquire sql.Conn") {
    // reduce concurrent pool usage, retry
  }
  return err
}

Prevention

When it happens

Trigger: Calling UpTo on a SQLite database whose connection pool cannot hand out a connection: context deadline exceeded, context canceled, or all pooled connections busy / pool closed.

Common situations: Parent request context timing out during a long restore; database already closed; connection pool exhausted by concurrent operations; restore path invoked during app shutdown.

Related errors


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