kubernetes/kops · error

error while waiting for volume %s to detach: %s

Error message

error while waiting for volume %s to detach: %s

What it means

Fires in deleteVolume when waitForDetach fails after DetachByDropletID was accepted — the volume detach action did not complete in time or errored while waiting, so deletion is aborted to avoid destroying an attached volume.

Source

Thrown at pkg/resources/digitalocean/resources.go:293

	return nil
}

func deleteVolume(cloud fi.Cloud, t *resources.Resource) error {
	c := cloud.(do.DOCloud)
	volume := t.Obj.(godo.Volume)
	for _, dropletID := range volume.DropletIDs {
		action, resp, err := c.VolumeActionService().DetachByDropletID(context.TODO(), volume.ID, dropletID)
		if err != nil {
			if resp != nil && resp.StatusCode == http.StatusNotFound {
				// Volume is already detached, nothing to do.
				continue
			}
			return fmt.Errorf("failed to detach volume %s: %s", volume.ID, err)
		}

		if err := waitForDetach(c, action); err != nil {
			return fmt.Errorf("error while waiting for volume %s to detach: %s", volume.ID, err)
		}
	}

	_, err := c.VolumeService().DeleteVolume(context.TODO(), t.ID)
	if err != nil {
		return fmt.Errorf("failed to delete volume: %s, err: %s", t.ID, err)
	}

	return nil
}

func deleteRecord(cloud fi.Cloud, domain string, t *resources.Resource) error {
	c := cloud.(do.DOCloud)
	record := t.Obj.(godo.DomainRecord)

	_, err := c.DomainService().DeleteRecord(context.TODO(), domain, record.ID)
	if err != nil {
		return fmt.Errorf("failed to delete record for domain %s: %d", domain, record.ID)

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Wait for the detach action to complete in DigitalOcean and retry deletion
  2. Check the volume's droplet attachments via the DO console
  3. If the detach action failed, inspect the action's error in the DO API

Example fix

// before
# action stuck "in-progress" until timeout
// after
# stop the droplet or force-detach manually
doctl compute droplet-action power-off <droplet-id>
kops delete cluster --cloud digitalocean ...
Defensive patterns

Strategy: retry

Validate before calling

deadline := time.Now().Add(5 * time.Minute)
for time.Now().Before(deadline) {
	a, _, err := c.ActionsService().Get(context.TODO(), action.ID)
	if err != nil { return err }
	if a.Status == godo.ActionCompleted { return nil }
	time.Sleep(10 * time.Second)
}
return errors.New("timed out waiting for volume detach")

Try / catch

if err := waitForDetach(c, action); err != nil {
	// one re-poll before failing
	time.Sleep(30 * time.Second)
	if err2 := waitForDetach(c, action); err2 != nil {
		return fmt.Errorf("error while waiting for volume %s to detach: %w", volume.ID, err2)
	}
}

Prevention

When it happens

Trigger: waitForDetach polls the action status and returns error on action failure, polling API error, or timeout while the detach stays in-progress.

Common situations: DO action stuck/failing due to heavy I/O on the volume; polling hit rate limits; temporary API outage during the wait loop.

Related errors


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