weaviate/weaviate · error

backup %q already exists at %q

Error message

backup %q already exists at %q

What it means

During backup validation the coordinator found existing backup metadata (backup_config.json) at the destination bucket/path whose status is not Cancelled — meaning a backup with that ID (or any backup occupying that slot) already exists in a valid/completed/failed-in-progress state. The scheduler refuses to overwrite; backups are identified by their destination slot, so a new backup must use a fresh ID or path.

Source

Thrown at usecases/backup/scheduler.go:867

		known[r] = struct{}{}
	}
	for _, r := range roles {
		if _, ok := known[r]; !ok {
			return nil, fmt.Errorf("role %q in 'includeRoles' does not exist", r)
		}
	}
	if len(roles) == 0 {
		return nil, fmt.Errorf("no roles match 'includeRoles' %v", includeRoles)
	}
	return roles, nil
}

func (s *Scheduler) checkIfBackupExists(ctx context.Context, store coordStore, req *BackupRequest) error {
	destPath := store.HomeDir(req.Bucket, req.Path)
	// there is no backup with given id on the backend, regardless of its state (valid or corrupted)
	meta, err := store.Meta(ctx, GlobalBackupFile, req.Bucket, req.Path)
	if err == nil && meta.Status != backup.Cancelled {
		return fmt.Errorf("backup %q already exists at %q", req.ID, destPath)
	}

	if !errors.As(err, &backup.ErrNotFound{}) {
		return fmt.Errorf("check if backup %q exists at %q: %w", req.ID, destPath, err)
	}
	return nil
}

func (s *Scheduler) validateRestoreRequest(ctx context.Context, store coordStore, req *BackupRequest) (*backup.DistributedBackupDescriptor, error) {
	if !store.backend.IsExternal() && s.restorer.nodeResolver.NodeCount() > 1 {
		return nil, errLocalBackendDBRO
	}
	if len(req.Include) > 0 && len(req.Exclude) > 0 {
		return nil, errIncludeExclude
	}
	// Check for duplicates in raw patterns early (before backend operations)
	if dup := findDuplicate(req.Include); dup != "" {
		return nil, fmt.Errorf("class list 'include' contains duplicate: %s", dup)

View on GitHub (pinned to 75aa4b6d11)

Solutions

  1. Choose a new, unique backup ID (or a different path) and retry.
  2. If the previous backup is no longer needed, delete/cancel it first (cancel moves it to Cancelled status, which this check permits to be overwritten), then re-run.
  3. If the slot contains stale/corrupted metadata from an aborted operation, remove the backup slot at destPath on the backend storage, then retry.

Example fix

// before
POST /v1/backups/s3 {"id": "daily-backup"}  // already exists
// after
POST /v1/backups/s3 {"id": "daily-backup-2026-09-04"}  // unique ID
Defensive patterns

Strategy: validation

Validate before calling

meta, err := store.Meta(ctx, "backup_config.json", bucket, path)
slotFree := err != nil || meta.Status == "CANCELLED"
if !slotFree {
    return fmt.Errorf("slot %s/%s occupied; use a new backup ID", bucket, path)
}

Try / catch

if err := createBackup(req); err != nil && strings.Contains(err.Error(), "already exists at") {
    // cancel/delete the old backup or pick a new ID before retrying
    newID := req.ID + "-" + time.Now().UTC().Format("20060102-150405")
    req.ID = newID
    return createBackup(req)
}

Prevention

When it happens

Trigger: Calling the backup API (create) with an ID whose destination (bucket+path) already holds metadata from a previous backup that succeeded, is running, or failed but wasn't cancelled. Also hit when re-running a backup job that already completed.

Common situations: Re-running a scheduled backup script with the same backup ID without cleanup; retrying after a failed run that left recoverable metadata; two pipelines racing with the same ID; restoring from an old config that still references a used ID.

Related errors


AI-assisted analysis of weaviate/weaviate@75aa4b6d11 (2026-09-04). Data as JSON: /api/errors/447ccd2dd5530679. Report an issue: GitHub.