golang-migrate/migrate · error

source driver: invalid URL scheme

Error message

source driver: invalid URL scheme

What it means

Raised by source.Open in source/driver.go when the parsed migration source URL has an empty URL scheme. The driver registry is keyed by scheme, so without a scheme the library cannot select a source driver and refuses to proceed.

Source

Thrown at source/driver.go:83

	ReadUp(version uint) (r io.ReadCloser, identifier string, err error)

	// ReadDown returns the DOWN migration body and an identifier that helps
	// finding this migration in the source for a given version.
	// If there is no down migration available for this version,
	// it must return os.ErrNotExist.
	// Do not start reading, just return the ReadCloser!
	ReadDown(version uint) (r io.ReadCloser, identifier string, err error)
}

// Open returns a new driver instance.
func Open(url string) (Driver, error) {
	u, err := nurl.Parse(url)
	if err != nil {
		return nil, err
	}

	if u.Scheme == "" {
		return nil, fmt.Errorf("source driver: invalid URL scheme")
	}

	driversMu.RLock()
	d, ok := drivers[u.Scheme]
	driversMu.RUnlock()
	if !ok {
		return nil, fmt.Errorf("source driver: unknown driver '%s' (forgotten import?)", u.Scheme)
	}

	return d.Open(url)
}

// Register globally registers a driver.
func Register(name string, driver Driver) {
	driversMu.Lock()
	defer driversMu.Unlock()
	if driver == nil {
		panic("Register driver is nil")

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Prefix the URL with its scheme, e.g. 'github://', 'file://', 's3://'
  2. Verify the env var or config value supplying the URL is not empty/truncated
  3. Log or print the URL right before source.Open to confirm what is actually passed

Example fix

// before
source.Open("github.com/user/repo/migrations")
// after
source.Open("github://user:token@github.com/user/repo/migrations")
Defensive patterns

Strategy: validation

Validate before calling

u, err := url.Parse(sourceURL)
if err != nil {
    return fmt.Errorf("bad source url: %w", err)
}
if u.Scheme == "" {
    return fmt.Errorf("source url %q missing scheme (want e.g. github://, file://)", sourceURL)
}

Type guard

func hasScheme(raw string) bool {
    u, err := url.Parse(raw)
    return err == nil && u.Scheme != ""
}

Try / catch

d, err := source.Open(sourceURL)
if err != nil {
    if strings.Contains(err.Error(), "invalid URL scheme") {
        return fmt.Errorf("check source URL %q: %w", sourceURL, err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling source.Open (or migrate.New with a source URL string) with a URL that has no 'scheme://' prefix, e.g. Open('github.com/user/repo') instead of Open('github://user:token@github.com/user/repo'), or a URL built by string concatenation that lost its prefix.

Common situations: Hand-written config files where the scheme was accidentally dropped, environment variables expanded to empty (missing MIGRATE_SOURCE env prefix), or URLs copied from docs with the scheme omitted.

Related errors


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