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
- Point the URL path at the directory (folder) containing the migrations, not an individual file: bitbucket://user:pass@owner/repo/migrations#ref.
- Remove any trailing filename from the configured path.
- Verify in the Bitbucket UI that the path is a folder containing migration files.
- 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
- Point the source URL at the folder containing migrations.
- Strip any trailing filename from configured paths.
- Confirm the path in the Bitbucket UI is a directory.
- Keep one canonical migrations directory per repo.
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
- x-migrations-table must be quoted (for instance '"migrate"."
- unable to parse option x-multi-statement: %w
- "%s" MigrationsTable contains too many dot characters
- no username:password provided
- no password/app password
AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02).
Data as JSON: /api/errors/c34863c5b9eac48e.
Report an issue: GitHub.