{"record":{"id":"d83e698c1b034748","repo":"golang-migrate/migrate","slug":"no-directory-d83e69","errorCode":null,"errorMessage":"no directory","messagePattern":"no directory","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"source/github/github.go","lineNumber":28,"sourceCode":"\t\"path\"\n\t\"strings\"\n\n\t\"golang.org/x/oauth2\"\n\n\t\"github.com/golang-migrate/migrate/v4/source\"\n\t\"github.com/google/go-github/v39/github\"\n)\n\nfunc init() {\n\tsource.Register(\"github\", &Github{})\n}\n\nvar (\n\tErrNoUserInfo          = fmt.Errorf(\"no username:token provided\")\n\tErrNoAccessToken       = fmt.Errorf(\"no access token\")\n\tErrInvalidRepo         = fmt.Errorf(\"invalid repo\")\n\tErrInvalidGithubClient = fmt.Errorf(\"expected *github.Client\")\n\tErrNoDir               = fmt.Errorf(\"no directory\")\n)\n\ntype Github struct {\n\tconfig     *Config\n\tclient     *github.Client\n\toptions    *github.RepositoryContentGetOptions\n\tmigrations *source.Migrations\n}\n\ntype Config struct {\n\tOwner string\n\tRepo  string\n\tPath  string\n\tRef   string\n}\n\nfunc (g *Github) Open(url string) (source.Driver, error) {\n\tu, err := nurl.Parse(url)","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/golang-migrate/migrate/blob/01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a/source/github/github.go#L10-L46","documentation":"github.ErrNoDir is returned by Github.readDirectory when the GitHub Contents API returns a single file instead of directory contents, meaning the configured path points at a file, not a directory of migrations.","triggerScenarios":"Configuring Path (or the URL path) to a single file such as .../migrations/0001_init.sql; the API returns fileContent != nil and readDirectory returns ErrNoDir.","commonSituations":"Typos in the migrations path; pointing at a symlink or a path that was moved; confusing file URL vs directory URL when copying from a GitHub blob URL.","solutions":["Point the path at the migrations directory, not an individual file (path without a filename)","Verify the directory exists on the configured Ref (branch/tag/sha) via the GitHub UI or API","If the repo keeps migrations at root or a subdir, adjust the URL path segment after owner/repo"],"exampleFix":"// before\nsource.Open(\"github://user:token@github.com/owner/repo/migrations/0001_init.up.sql\")\n// after\nsource.Open(\"github://user:token@github.com/owner/repo/migrations\")","handlingStrategy":"validation","validationCode":"// before opening, confirm the path is a directory on the ref:\n_, dir, _, err := client.Repositories.GetContents(ctx, owner, repo, path, &github.RepositoryContentGetOptions{Ref: ref})\nif err != nil {\n    return err\n}\nif dir == nil {\n    return fmt.Errorf(\"%s on ref %s is a file, not a directory\", path, ref)\n}","typeGuard":"func isDirectoryContent(fc *github.RepositoryContent, dir []*github.RepositoryContent) bool {\n    return fc == nil && len(dir) > 0\n}","tryCatchPattern":"d, err := source.Open(srcURL)\nif err != nil {\n    if errors.Is(err, source_github.ErrNoDir) {\n        return fmt.Errorf(\"path in %q points to a file; use the migrations directory\", srcURL)\n    }\n    return err\n}","preventionTips":["Strip filenames from GitHub blob URLs; keep only the directory path","Confirm the directory exists on the exact ref/branch configured","Add a smoke check that lists the directory at startup"],"tags":["github","configuration","path"],"backgroundTag":"path-is-not-a-directory","analyzedSha":"01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a","analyzedAt":"2026-09-02T19:38:29.671Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}