{"record":{"id":"c34863c5b9eac48e","repo":"golang-migrate/migrate","slug":"no-directory","errorCode":null,"errorMessage":"no directory","messagePattern":"no directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"source/bitbucket/bitbucket.go","lineNumber":25,"sourceCode":"\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/golang-migrate/migrate/v4/source\"\n\t\"github.com/ktrysmt/go-bitbucket\"\n)\n\nfunc init() {\n\tsource.Register(\"bitbucket\", &Bitbucket{})\n}\n\nvar (\n\tErrNoUserInfo             = fmt.Errorf(\"no username:password provided\")\n\tErrNoAccessToken          = fmt.Errorf(\"no password/app password\")\n\tErrInvalidRepo            = fmt.Errorf(\"invalid repo\")\n\tErrInvalidBitbucketClient = fmt.Errorf(\"expected *bitbucket.Client\")\n\tErrNoDir                  = fmt.Errorf(\"no directory\")\n)\n\ntype Bitbucket struct {\n\tconfig     *Config\n\tclient     *bitbucket.Client\n\tmigrations *source.Migrations\n}\n\ntype Config struct {\n\tOwner string\n\tRepo  string\n\tPath  string\n\tRef   string\n}\n\nfunc (b *Bitbucket) Open(url string) (source.Driver, error) {\n\tu, err := nurl.Parse(url)\n\tif err != nil {","sourceCodeStart":7,"sourceCodeEnd":43,"githubUrl":"https://github.com/golang-migrate/migrate/blob/01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a/source/bitbucket/bitbucket.go#L7-L43","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nbitbucket://user:pass@team/repo/migrations/1_init.up.sql\n// after\nbitbucket://user:pass@team/repo/migrations#master","handlingStrategy":"validation","validationCode":"u, _ := nurl.Parse(sourceURL)\nbase := path.Base(strings.Trim(u.Path, \"/\"))\nif strings.Contains(base, \".\") && (strings.HasSuffix(base, \".up.sql\") || strings.HasSuffix(base, \".down.sql\")) {\n    return fmt.Errorf(\"source path %q points at a file; use the migrations directory\", u.Path)\n}","typeGuard":"func isDirectoryLikePath(p string) bool {\n    return p != \"\" && !strings.HasPrefix(path.Base(p), \"\") && path.Ext(p) == \"\"\n}","tryCatchPattern":"d, err := bitbucket.Open(url)\nif errors.Is(err, bitbucket.ErrNoDir) {\n    return fmt.Errorf(\"source path must be a directory of migrations, not a single file\")\n}","preventionTips":["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."],"tags":["bitbucket","path","config","migrations"],"backgroundTag":"source-path-not-directory","analyzedSha":"01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a","analyzedAt":"2026-09-02T19:38:29.671Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}