golang-migrate/migrate · error

open() cannot be called on the iofs passthrough driver

Error message

open() cannot be called on the iofs passthrough driver

What it means

The iofs package wraps an io/fs.FS as a migration source. Its driver's Open method is deliberately unimplemented — instances are created by WithInstance(fsys), not through the URL registry — so any call to Open returns this error. It exists only to complete the source.Driver interface.

Source

Thrown at source/iofs/iofs.go:32

)

type driver struct {
	PartialDriver
}

// New returns a new Driver from io/fs#FS and a relative path.
func New(fsys fs.FS, path string) (source.Driver, error) {
	var i driver
	if err := i.Init(fsys, path); err != nil {
		return nil, fmt.Errorf("failed to init driver with path %s: %w", path, err)
	}
	return &i, nil
}

// Open is part of source.Driver interface implementation.
// Open cannot be called on the iofs passthrough driver.
func (d *driver) Open(url string) (source.Driver, error) {
	return nil, errors.New("open() cannot be called on the iofs passthrough driver")
}

// PartialDriver is a helper service for creating new source drivers working with
// io/fs.FS instances. It implements all source.Driver interface methods
// except for Open(). New driver could embed this struct and add missing Open()
// method.
//
// To prepare PartialDriver for use Init() function.
type PartialDriver struct {
	migrations *source.Migrations
	fsys       fs.FS
	path       string
}

// Init prepares not initialized IoFS instance to read migrations from a
// io/fs#FS instance and a relative path.
func (d *PartialDriver) Init(fsys fs.FS, path string) error {
	entries, err := fs.ReadDir(fsys, path)

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Use iofs.WithInstance(fsys) (e.g. with an embed.FS) and pass the result via migrate.NewWithSourceInstance("iofs", d, databaseURL).
  2. When embedding PartialDriver in a custom driver, override Open with a real constructor that returns your driver for valid URLs.
  3. Do not register the passthrough driver in the URL scheme registry.

Example fix

// before
m, err := migrate.New("iofs://embedded", dsn)
// after
d, err := iofs.WithInstance(migrationsFS)
m, err := migrate.NewWithSourceInstance("iofs", d, dsn)
Defensive patterns

Strategy: validation

Validate before calling

d, err := iofs.WithInstance(embeddedFS)
if err != nil { return err }
m, err := migrate.NewWithSourceInstance("iofs", d, databaseURL)

Try / catch

if err != nil && strings.Contains(err.Error(), "iofs passthrough") {
    return fmt.Errorf("iofs drivers must be created with iofs.WithInstance, not Open")
}

Prevention

When it happens

Trigger: Calling Open on the driver returned by iofs.WithInstance; registering the iofs driver under a URL scheme and then invoking source.Open("iofs://...").

Common situations: Trying to use embed.FS-based migrations through the URL-based migrate.New API instead of migrate.NewWithSourceInstance; wrapping the passthrough driver in another driver and delegating Open to it.

Related errors


AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02). Data as JSON: /api/errors/e533434598cb93fb. Report an issue: GitHub.