golang-migrate/migrate · error

no directory

Error message

no directory

What it means

ErrNoDir signals that the configured source location resolved to a single file rather than a directory of migrations. In the bitbucket driver it is raised by readDirectory when the repository listing returns file content (a single file) instead of a directory listing; the github driver uses it identically. Exported sentinel error comparable with errors.Is.

Source

Thrown at source/bitbucket/bitbucket.go:25

	"os"
	"path"
	"path/filepath"
	"strings"

	"github.com/golang-migrate/migrate/v4/source"
	"github.com/ktrysmt/go-bitbucket"
)

func init() {
	source.Register("bitbucket", &Bitbucket{})
}

var (
	ErrNoUserInfo             = fmt.Errorf("no username:password provided")
	ErrNoAccessToken          = fmt.Errorf("no password/app password")
	ErrInvalidRepo            = fmt.Errorf("invalid repo")
	ErrInvalidBitbucketClient = fmt.Errorf("expected *bitbucket.Client")
	ErrNoDir                  = fmt.Errorf("no directory")
)

type Bitbucket struct {
	config     *Config
	client     *bitbucket.Client
	migrations *source.Migrations
}

type Config struct {
	Owner string
	Repo  string
	Path  string
	Ref   string
}

func (b *Bitbucket) Open(url string) (source.Driver, error) {
	u, err := nurl.Parse(url)
	if err != nil {

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Point the URL path at the directory (folder) containing the migrations, not an individual file: bitbucket://user:pass@owner/repo/migrations#ref.
  2. Remove any trailing filename from the configured path.
  3. Verify in the Bitbucket UI that the path is a folder containing migration files.
  4. Compare with errors.Is(err, bitbucket.ErrNoDir) to emit a 'path must be a directory' message in tooling.

Example fix

// before
bitbucket://user:pass@team/repo/migrations/1_init.up.sql
// after
bitbucket://user:pass@team/repo/migrations#master
Defensive patterns

Strategy: validation

Validate before calling

u, _ := nurl.Parse(sourceURL)
base := path.Base(strings.Trim(u.Path, "/"))
if strings.Contains(base, ".") && (strings.HasSuffix(base, ".up.sql") || strings.HasSuffix(base, ".down.sql")) {
    return fmt.Errorf("source path %q points at a file; use the migrations directory", u.Path)
}

Type guard

func isDirectoryLikePath(p string) bool {
    return p != "" && !strings.HasPrefix(path.Base(p), "") && path.Ext(p) == ""
}

Try / catch

d, err := bitbucket.Open(url)
if errors.Is(err, bitbucket.ErrNoDir) {
    return fmt.Errorf("source path must be a directory of migrations, not a single file")
}

Prevention

When it happens

Trigger: Pointing the source URL's path at a single file (e.g. bitbucket://user:pass@owner/repo/migrations/1_init.up.sql) so the repo API returns a file object, causing the driver to return ErrNoDir instead of enumerating migrations.

Common situations: Typos in the configured path that land on a file; intentionally pointing at one migration file expecting migrate to apply it; copy-pasting a file URL into a directory-expecting source driver.

Related errors


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