kubernetes/kops · error
failed to delete server %s(%s): %w
Error message
failed to delete server %s(%s): %w
What it means
Wrapped error from deleteServer: the hcloud ServerClient.DeleteWithResult call failed when kOps deletes a cluster server. Unlike simple deletes, this uses DeleteWithResult and then watches the action via WatchProgress; this specific message only covers the initial DeleteWithResult failure, while a failed action watch surfaces as the raw error from errCh.
Source
Thrown at pkg/resources/hetzner/resources.go:291
return fmt.Errorf("failed to delete load balancer %s(%s): %w", r.Name, r.ID, err)
}
return nil
}
func deleteServer(cloud fi.Cloud, r *resources.Resource) error {
ctx := context.TODO()
klog.Infof("Deleting Server: %s(%s)", r.Name, r.ID)
c := cloud.(hetzner.HetznerCloud)
serverClient := c.ServerClient()
actionClient := c.ActionClient()
server := r.Obj.(*hcloud.Server)
result, _, err := serverClient.DeleteWithResult(ctx, server)
if err != nil {
return fmt.Errorf("failed to delete server %s(%s): %w", r.Name, r.ID, err)
}
_, 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 {View on GitHub (pinned to 4c8573c808)
Solutions
- Inspect the wrapped hcloud error; if it is not-found, the server is already gone and the cluster delete can be retried.
- Confirm the API token has server write permissions and is not expired.
- Wait for any running server operations (backups, rescues) to finish — locked servers cannot be deleted.
- If the initial delete succeeds but WatchProgress errors, rerun the delete; kOps refreshes server state each pass.
Defensive patterns
Strategy: try-catch
Validate before calling
srv, _, err := c.Server.Get(ctx, strconv.Itoa(serverID))
if err != nil { return err }
if srv == nil { return nil } // already deleted
if srv.Status == hcloud.ServerStatusStarting || srv.Locked() { // locked servers can't be deleted
return fmt.Errorf("server %d is busy/locked (status=%s)", serverID, srv.Status)
} Type guard
func isHcloudErrorWithCode(err error, code hcloud.ErrorCode) bool {
var hErr hcloud.Error
return errors.As(err, &hErr) && hErr.Code == code
} Try / catch
_, _, err := serverClient.DeleteWithResult(ctx, server)
if err != nil {
var hErr hcloud.Error
if errors.As(err, &hErr) && hErr.Code == hcloud.ErrorCodeNotFound {
return nil // already gone
}
if errors.As(err, &hErr) && hErr.Code == hcloud.ErrorCodeRateLimitExceeded {
return backoffRetry(err)
}
return fmt.Errorf("failed to delete server %s(%s): %w", name, id, err)
} Prevention
- Do not delete servers from the Hetzner console while kOps delete is running.
- Ensure no rescue mode, backups or other locks are active on servers before teardown.
- Use a token with server write scope.
- Rerun the delete if WatchProgress reports an error — server state is refreshed each pass.
When it happens
Trigger: serverClient.DeleteWithResult(ctx, server) returns an error: server already stopped/deleted (not-found), API token invalid, rate limit, or server locked (e.g. rescue/backup in progress).
Common situations: Server was terminated manually in the Hetzner console before kOps delete; Hetzner maintenance/lock on the server; token permissions lacking server write scope.
Related errors
- failed to delete network %s(%s): %w
- failed to delete firewall %s(%s): %w
- failed to delete load balancer %s(%s): %w
- failed to delete volume %s(%s): %w
- error deleting Subnetwork %s: %v
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/ecfbda8b878a3615.
Report an issue: GitHub.