{"record":{"id":"262d4c57c81ca374","repo":"wavetermdev/waveterm","slug":"backup-metadata-mismatch-expected-s-got-s","errorCode":null,"errorMessage":"backup metadata mismatch: expected %s, got %s","messagePattern":"backup metadata mismatch: expected (.+?), got (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/filebackup/filebackup.go","lineNumber":109,"sourceCode":"\tbackupData, err := os.ReadFile(backupFilePath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read backup file: %w\", err)\n\t}\n\n\tmetadataPath := backupFilePath[:len(backupFilePath)-4] + \".json\"\n\tmetadataData, err := os.ReadFile(metadataPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to read backup metadata: %w\", err)\n\t}\n\n\tvar metadata BackupMetadata\n\terr = json.Unmarshal(metadataData, &metadata)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to unmarshal backup metadata: %w\", err)\n\t}\n\n\tif metadata.FullPath != restoreToFileName {\n\t\treturn fmt.Errorf(\"backup metadata mismatch: expected %s, got %s\", restoreToFileName, metadata.FullPath)\n\t}\n\n\tvar perm os.FileMode\n\t_, err = fmt.Sscanf(metadata.Perm, \"%o\", &perm)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to parse file permissions: %w\", err)\n\t}\n\n\terr = os.WriteFile(restoreToFileName, backupData, perm)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to restore file: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc CleanupOldBackups() error {\n\tbackupBaseDir := filepath.Join(wavebase.GetWaveCachesDir(), \"waveai-backups\")","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/filebackup/filebackup.go#L91-L127","documentation":"RestoreBackup is a safety check: metadata.FullPath (the original absolute path recorded at backup time) must exactly equal the restoreToFileName argument. This error means the caller asked to restore a backup into a different file than it was taken from, so the library refuses to overwrite the wrong file. Note the 'expected/got' wording is inverted from what one might assume: 'expected' is restoreToFileName, 'got' is metadata.FullPath.","triggerScenarios":"Calling RestoreBackup with a restoreToFileName that differs byte-for-byte from the FullPath stored in the .json sidecar — e.g. relative vs absolute path, symlinked vs real path, path case differences, or the file was moved since backup.","commonSituations":"Passing a relative path like \"notes.txt\" instead of the absolute path stored at backup time; the project directory was moved/renamed; resolving through a symlink or bind mount so the string differs; renaming the file and trying to restore under the new name.","solutions":["Pass the exact absolute path recorded in the .json sidecar's 'fullpath' field as restoreToFileName.","If the original path legitimately moved, update the sidecar's fullpath (or the intended target logic) deliberately rather than bypassing the check.","Normalize the path (filepath.Abs / EvalSymlinks) on both sides before comparing if callers may use different but equivalent forms.","Read the .json sidecar to see what FullPath was recorded and reconcile which path is correct."],"exampleFix":"// before\nfilebackup.RestoreBackup(bakPath, \"notes.txt\") // relative path mismatches\n// after\ntarget, _ := filepath.Abs(\"notes.txt\")\nfilebackup.RestoreBackup(bakPath, target) // matches metadata.FullPath","handlingStrategy":"validation","validationCode":"metaData, _ := os.ReadFile(strings.TrimSuffix(backupFilePath, \".bak\") + \".json\")\nvar meta struct{ FullPath string `json:\"fullpath\"` }\njson.Unmarshal(metaData, &meta)\ntarget, _ := filepath.Abs(restoreTo)\nif meta.FullPath != target {\n    return fmt.Errorf(\"restore target %q does not match backup origin %q\", target, meta.FullPath)\n}","typeGuard":null,"tryCatchPattern":"err := filebackup.RestoreBackup(backupPath, targetPath)\nif err != nil {\n    if strings.Contains(err.Error(), \"metadata mismatch\") {\n        // read the sidecar's 'fullpath' and use it as the restore target\n        return fmt.Errorf(\"wrong restore target: %w\", err)\n    }\n    return err\n}","preventionTips":["Always pass the exact absolute path from the sidecar's 'fullpath' field","Resolve symlinks and make paths absolute before comparing","Don't attempt to restore a moved/renamed file under its new path without updating metadata","Remember 'expected' in the message is your restoreToFileName, 'got' is the stored FullPath"],"tags":["validation","path","restore","mismatch"],"backgroundTag":"path-mismatch","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}