{"record":{"id":"f5ccc71000df6e00","repo":"wavetermdev/waveterm","slug":"failed-to-read-backup-metadata-w","errorCode":null,"errorMessage":"failed to read backup metadata: %w","messagePattern":"failed to read backup metadata: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/filebackup/filebackup.go","lineNumber":99,"sourceCode":"\n\terr = os.WriteFile(metadataPath, metadataJSON, 0600)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to write backup metadata: %w\", err)\n\t}\n\n\treturn backupPath, nil\n}\n\nfunc RestoreBackup(backupFilePath string, restoreToFileName string) error {\n\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","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/filebackup/filebackup.go#L81-L117","documentation":"RestoreBackup derives the metadata sidecar path by replacing the .bak suffix with .json and reads it. This error wraps os.ReadFile failing on that .json file; without metadata (full path and permissions) the restore cannot proceed safely.","triggerScenarios":"The paired <name>.<hash>.<uuid>.json file is missing (deleted individually, partial backup from error 703 scenario, or a .bak file passed without its sidecar ever having been written).","commonSituations":"A prior backup half-failed leaving an orphan .bak; users or cleanup scripts deleting *.json files; restoring a .bak copied elsewhere without its sidecar.","solutions":["Verify the .json sidecar exists next to the .bak file (same base name) before restoring.","If the sidecar is gone, locate another backup of the same file in the dated directories that has both .bak and .json.","Avoid copying/moving .bak files without their .json sidecars.","Check cleanup scripts or manual deletions that remove .json files independently."],"exampleFix":"// before\nos.Remove(backupPath + \".json\") // 'cleanup' breaks restore\n// after\n// keep .bak and .json together: remove by shared base name\nos.Remove(strings.TrimSuffix(backupPath, \".bak\") + \".json\") // only when removing both","handlingStrategy":"validation","validationCode":"metaPath := strings.TrimSuffix(backupFilePath, \".bak\") + \".json\"\nif _, err := os.Stat(metaPath); err != nil {\n    return fmt.Errorf(\"metadata sidecar %s missing; backup pair incomplete\", metaPath)\n}","typeGuard":null,"tryCatchPattern":"err := filebackup.RestoreBackup(backupPath, targetPath)\nif err != nil {\n    if strings.Contains(err.Error(), \"read backup metadata\") {\n        return fmt.Errorf(\"backup pair incomplete (.json sidecar missing): %w\", err)\n    }\n    return err\n}","preventionTips":["Always treat .bak and .json as an atomic pair","Skip custom cleanup rules that single out .json files","Check for half-failed backups (orphan .bak) after disk-full events","Verify the sidecar exists before offering restore to users"],"tags":["filesystem","file-not-found","metadata","restore"],"backgroundTag":"file-not-found","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}