wavetermdev/waveterm · error

backup metadata mismatch: expected %s, got %s

Error message

backup metadata mismatch: expected %s, got %s

What it means

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.

Source

Thrown at pkg/filebackup/filebackup.go:109

	backupData, err := os.ReadFile(backupFilePath)
	if err != nil {
		return fmt.Errorf("failed to read backup file: %w", err)
	}

	metadataPath := backupFilePath[:len(backupFilePath)-4] + ".json"
	metadataData, err := os.ReadFile(metadataPath)
	if err != nil {
		return fmt.Errorf("failed to read backup metadata: %w", err)
	}

	var metadata BackupMetadata
	err = json.Unmarshal(metadataData, &metadata)
	if err != nil {
		return fmt.Errorf("failed to unmarshal backup metadata: %w", err)
	}

	if metadata.FullPath != restoreToFileName {
		return fmt.Errorf("backup metadata mismatch: expected %s, got %s", restoreToFileName, metadata.FullPath)
	}

	var perm os.FileMode
	_, err = fmt.Sscanf(metadata.Perm, "%o", &perm)
	if err != nil {
		return fmt.Errorf("failed to parse file permissions: %w", err)
	}

	err = os.WriteFile(restoreToFileName, backupData, perm)
	if err != nil {
		return fmt.Errorf("failed to restore file: %w", err)
	}

	return nil
}

func CleanupOldBackups() error {
	backupBaseDir := filepath.Join(wavebase.GetWaveCachesDir(), "waveai-backups")

View on GitHub (pinned to a4447c1563)

Solutions

  1. Pass the exact absolute path recorded in the .json sidecar's 'fullpath' field as restoreToFileName.
  2. If the original path legitimately moved, update the sidecar's fullpath (or the intended target logic) deliberately rather than bypassing the check.
  3. Normalize the path (filepath.Abs / EvalSymlinks) on both sides before comparing if callers may use different but equivalent forms.
  4. Read the .json sidecar to see what FullPath was recorded and reconcile which path is correct.

Example fix

// before
filebackup.RestoreBackup(bakPath, "notes.txt") // relative path mismatches
// after
target, _ := filepath.Abs("notes.txt")
filebackup.RestoreBackup(bakPath, target) // matches metadata.FullPath
Defensive patterns

Strategy: validation

Validate before calling

metaData, _ := os.ReadFile(strings.TrimSuffix(backupFilePath, ".bak") + ".json")
var meta struct{ FullPath string `json:"fullpath"` }
json.Unmarshal(metaData, &meta)
target, _ := filepath.Abs(restoreTo)
if meta.FullPath != target {
    return fmt.Errorf("restore target %q does not match backup origin %q", target, meta.FullPath)
}

Try / catch

err := filebackup.RestoreBackup(backupPath, targetPath)
if err != nil {
    if strings.Contains(err.Error(), "metadata mismatch") {
        // read the sidecar's 'fullpath' and use it as the restore target
        return fmt.Errorf("wrong restore target: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/262d4c57c81ca374. Report an issue: GitHub.