{"record":{"id":"5fa795a572958cae","repo":"pingcap/tidb","slug":"invalid-backup-data-file-name-s-5fa795","errorCode":null,"errorMessage":"invalid backup data file name: '%s'","messagePattern":"invalid backup data file name: '(.+?)'","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"br/pkg/restore/snap_client/tikv_sender.go","lineNumber":388,"sourceCode":"\tsplitter := split.NewRegionSplitterWithRegionIndexStep(split.NewClient(\n\t\trc.pdClient,\n\t\trc.pdHTTPClient,\n\t\trc.tlsConf,\n\t\tmaxSplitKeysOnce,\n\t\trc.storeCount+1,\n\t\tsplitClientOpts...,\n\t), rc.splitRegionIndexStep)\n\tsplitter.SetCoarseScatter(rc.coarseScatter)\n\n\treturn splitter.ExecuteSortedKeys(ctx, sortedSplitKeys)\n}\n\nfunc getFileRangeKey(f string) string {\n\t// the backup date file pattern is `{store_id}_{region_id}_{epoch_version}_{key}_{ts}_{cf}.sst`\n\t// so we need to compare with out the `_{cf}.sst` suffix\n\tidx := strings.LastIndex(f, \"_\")\n\tif idx < 0 {\n\t\tpanic(fmt.Sprintf(\"invalid backup data file name: '%s'\", f))\n\t}\n\n\treturn f[:idx]\n}\n\nfunc (rc *SnapClient) sendRequestToStore(\n\tctx context.Context,\n\tsendFn func(ectx context.Context, client importclient.ImporterClient, storeId uint64) error,\n) error {\n\tstores, err := conn.GetAllTiKVStoresWithRetry(ctx, rc.pdClient, util.SkipTiFlash)\n\tif err != nil {\n\t\treturn errors.Trace(err)\n\t}\n\teg, ectx := errgroup.WithContext(ctx)\n\tpool := tidbutil.NewWorkerPool(uint(len(stores)), \"check and compact\")\n\tfor _, store := range stores {\n\t\tif store.StatusAddress == \"\" || store.State != metapb.StoreState_Up {\n\t\t\tcontinue","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/pingcap/tidb/blob/d01f9615c13e02dfa75922745640dec9d81b802e/br/pkg/restore/snap_client/tikv_sender.go#L370-L406","documentation":"Same invariant as restorer.go's GetFileRangeKey, duplicated in the snap-restore client (tikv_sender.go): backup data file names must look like '{store_id}_{region_id}_{epoch_version}_{key}_{ts}_{cf}.sst', and the range key is the name minus the '_{cf}.sst' suffix. A name with no underscore at all makes strings.LastIndex return -1 and triggers this panic.","triggerScenarios":"Snapshot restore reaching getFileRangeKey while building file groups / sorted split keys over the backup sst set, and encountering a file name without any '_' character - a stray file in the sst prefix, a truncated upload, or a renamed object in S3/GCS.","commonSituations":"Extra objects placed in the backup storage bucket next to the sst files; backups copied with tools that mangle names; interrupted uploads leaving non-canonical files; mismatch between the BR version that wrote the backup and the one restoring it.","solutions":["List the backup sst prefix and delete or exclude files whose names do not match the canonical six-part underscore pattern.","Re-run the restore from an untouched backup produced and read by the same BR/TiDB version.","Validate the backup ('br backup validate' or restore dry-run) before the real restore.","If BR itself wrote the offending name, collect the file list and backupmeta and file a bug at github.com/pingcap/br."],"exampleFix":"// before\nrangeKey := getFileRangeKey(f) // panics on 'README' or 'dat.sst'\n\n// after: filter to canonical data files first\nvar reBackupSST = regexp.MustCompile(`^\\d+_\\d+_\\d+_.+_.+_\\w+\\.sst$`)\nif !reBackupSST.MatchString(f) {\n    return nil\n}\nrangeKey := getFileRangeKey(f)","handlingStrategy":"validation","validationCode":"var reBackupSST = regexp.MustCompile(`^\\d+_\\d+_\\d+_.+_.+_\\w+\\.sst$`)\n\n// filter the sst listing before passing names into restore/split logic\nfunc filterCanonicalSSTs(names []string) []string {\n    out := make([]string, 0, len(names))\n    for _, n := range names {\n        if reBackupSST.MatchString(n) {\n            out = append(out, n)\n        }\n    }\n    return out\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write backups to a dedicated storage prefix with no foreign objects.","Verify the file listing matches backupmeta before restore (checksums/counts).","Use matching BR versions for backup and restore."],"tags":["br","restore","snapshot","filename","validation","panic"],"backgroundTag":null,"analyzedSha":"d01f9615c13e02dfa75922745640dec9d81b802e","analyzedAt":"2026-08-15T12:19:33.324Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}