kubernetes/kops · error

failed to delete volume

Error message

failed to delete volume %s(%s): %w

What it means

Wrapped error from deleteVolume: hcloud VolumeClient.Delete failed while kOps removes a cluster volume. Commonly the underlying error is hcloud's 'volume is still attached' conflict, since volumes must be detached (or their server deleted) first.

Solutions

  1. Detach the volume from its server (console: `hcloud volume detach`) or delete the attached server, then retry.
  2. Verify HCLOUD_TOKEN validity and write scope.
  3. If not-found, the volume is already gone — rerun delete.
  4. Retry after any rate-limit window indicated by the wrapped error.

Example fix

// before: attached volume blocks delete
client.Delete(context.TODO(), volume) // volume is still attached to server X
// after: detach first
_, _, _ = actionClient.AttachVolume(ctx, server, hcloud.VolumeAttachOpts{Volume: volume}) // opposite op used at create
// at teardown ensure servers are deleted before volumes (kOps ordering already does this);
// for manual cleanup: hcloud volume detach <id> && kops delete cluster ...
Defensive patterns

Strategy: try-catch

Validate before calling

vol, _, err := c.Volume.Get(ctx, strconv.Itoa(volumeID))
if err != nil { return err }
if vol == nil { return nil }
if vol.Server != nil {
    return fmt.Errorf("volume %d still attached to server %d", volumeID, vol.Server.ID)
}

Type guard

func isAttachedVolumeError(err error) bool {
    var hErr hcloud.Error
    return errors.As(err, &hErr) && hErr.Code == hcloud.ErrorCodeVolumeAttached
}

Try / catch

err := deleteVolume(...)
var hErr hcloud.Error
switch {
case err == nil:
    return nil
case errors.As(err, &hErr) && hErr.Code == hcloud.ErrorCodeNotFound:
    return nil
case errors.As(err, &hErr) && hErr.Code == hcloud.ErrorCodeVolumeAttached:
    return detachVolumeThenRetry(err)
default:
    return err
}

Prevention

When it happens

Trigger: client.Delete(ctx, volume) fails: volume attached to a server (hcloud conflict), invalid token, rate limit, or volume already deleted.

Common situations: Server crashed or was deleted out-of-band leaving attached volumes; PVC-backed volumes whose server teardown ordering raced; credential rotation mid-delete.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/1e3584bef76a91b5. Report an issue: GitHub.

Appendix: source

Thrown at pkg/resources/hetzner/resources.go:310

	}

	_, errCh := actionClient.WatchProgress(ctx, result.Action)
	if err := <-errCh; err != nil {
		return err
	}

	return nil
}

func deleteVolume(cloud fi.Cloud, r *resources.Resource) error {
	klog.Infof("Deleting Volume: %s(%s)", r.Name, r.ID)

	c := cloud.(hetzner.HetznerCloud)
	client := c.VolumeClient()
	volume := r.Obj.(*hcloud.Volume)
	_, err := client.Delete(context.TODO(), volume)
	if err != nil {
		return fmt.Errorf("failed to delete volume %s(%s): %w", r.Name, r.ID, err)
	}

	return nil
}

func dumpServer(op *resources.DumpOperation, r *resources.Resource) error {
	server := r.Obj.(*hcloud.Server)

	data := make(map[string]interface{})
	data["id"] = r.ID
	data["type"] = r.Type
	data["raw"] = r.Obj
	op.Dump.Resources = append(op.Dump.Resources, data)

	i := &resources.Instance{
		Name: r.ID,
	}
	if ip := server.PublicNet.IPv4.IP; ip != nil {

View on GitHub (pinned to 4c8573c808)