{"record":{"id":"f94af73e329c7c8b","repo":"golang-migrate/migrate","slug":"malformed-migration-filename-s","errorCode":null,"errorMessage":"malformed migration filename: %s","messagePattern":"malformed migration filename: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cli/commands.go","lineNumber":36,"sourceCode":"\terrInvalidSequenceWidth     = errors.New(\"digits must be positive\")\n\terrIncompatibleSeqAndFormat = errors.New(\"the seq and format options are mutually exclusive\")\n\terrInvalidTimeFormat        = errors.New(\"time format may not be empty\")\n)\n\nfunc nextSeqVersion(matches []string, seqDigits int) (string, error) {\n\tif seqDigits <= 0 {\n\t\treturn \"\", errInvalidSequenceWidth\n\t}\n\n\tnextSeq := uint64(1)\n\n\tif len(matches) > 0 {\n\t\tfilename := matches[len(matches)-1]\n\t\tmatchSeqStr := filepath.Base(filename)\n\t\tidx := strings.Index(matchSeqStr, \"_\")\n\n\t\tif idx < 1 { // Using 1 instead of 0 since there should be at least 1 digit\n\t\t\treturn \"\", fmt.Errorf(\"malformed migration filename: %s\", filename)\n\t\t}\n\n\t\tvar err error\n\t\tmatchSeqStr = matchSeqStr[0:idx]\n\t\tnextSeq, err = strconv.ParseUint(matchSeqStr, 10, 64)\n\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\n\t\tnextSeq++\n\t}\n\n\tversion := fmt.Sprintf(\"%0[2]*[1]d\", nextSeq, seqDigits)\n\n\tif len(version) > seqDigits {\n\t\treturn \"\", fmt.Errorf(\"next sequence number %s too large, at most %d digits are allowed\", version, seqDigits)\n\t}","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/golang-migrate/migrate/blob/01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a/internal/cli/commands.go#L18-L54","documentation":"nextSeqVersion derives the next sequence number by looking at the last existing migration filename in the directory and parsing the digits before the first underscore. If that filename has no underscore (or starts with one), the CLI cannot extract a sequence prefix and refuses to continue rather than guessing a version. This guards against mixing non-sequential files into a seq-numbered migrations directory.","triggerScenarios":"Running `migrate create -seq ...` in a directory where the highest-matching migration file (by glob `<dir>_*<ext>`, sorted lexically) has a name without `<digits>_`, e.g. `foo.up.sql` or `_init.up.sql`.","commonSituations":"A hand-named migration file was dropped into the migrations folder; a file created with the default timestamp format lives next to seq files; an editor/backup file like `~` or `.bak` variants match the glob; someone renamed a migration and removed the numeric prefix.","solutions":["Rename the offending file(s) in the migrations directory to the standard `<seq>_<name>.<direction><ext>` format, e.g. `3_add_users.up.sql`.","Move non-conforming files (notes, backups, timestamp-named migrations) out of the seq migrations directory.","List files with `ls` / `filepath.Glob(dir/*<ext>)` and verify every file starts with digits followed by an underscore.","Recreate the migration with `migrate create -seq` after cleaning the directory so the sequence can be computed."],"exampleFix":"// before\nmigrations/foo.up.sql\n// after\nmigrations/000001_foo.up.sql","handlingStrategy":"validation","validationCode":"files, _ := filepath.Glob(filepath.Join(dir, \"*\"+ext))\nfor _, f := range files {\n    base := filepath.Base(f)\n    idx := strings.Index(base, \"_\")\n    if idx < 1 {\n        return fmt.Errorf(\"%s must be named <digits>_<name>.<up|down>%s\", f, ext)\n    }\n    if _, err := strconv.ParseUint(base[:idx], 10, 64); err != nil {\n        return err\n    }\n}","typeGuard":"func hasSeqPrefix(name string) bool {\n    idx := strings.Index(name, \"_\")\n    if idx < 1 {\n        return false\n    }\n    _, err := strconv.ParseUint(name[:idx], 10, 64)\n    return err == nil\n}","tryCatchPattern":"if err := createCmd(dir, time.Now(), format, name, ext, seq, seqDigits, print); err != nil {\n    if strings.Contains(err.Error(), \"malformed migration filename\") {\n        // prompt to fix/renames the offending file named in the error\n    }\n    return err\n}","preventionTips":["Only add migrations via `migrate create -seq`, never by hand-naming files.","Keep notes/backups/timestamp-format files outside the seq migrations directory.","Add a CI lint that regex-checks every migration filename against ^\\d+_.+\\.(up|down)\\.<ext$.","Never rename a migration's numeric prefix."],"tags":["cli","migrations","naming","file-format"],"backgroundTag":"malformed-migration-filename","analyzedSha":"01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a","analyzedAt":"2026-09-02T19:38:29.671Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}