kubernetes/kops · error
failed to delete server %q: %w
Error message
failed to delete server %q: %w
What it means
Fires in deleteServer after the Hetzner hcloud API Delete call on an already-powered-off server returns an error. It wraps the underlying API failure (rate limit, auth, server not found, or server still running) so instance-deletion tasks surface why the Hetzner server could not be removed.
Source
Thrown at upup/pkg/fi/cloudup/hetzner/cloud.go:288
return fmt.Errorf("failed to stop server %q: %w", strconv.FormatInt(id, 10), err)
}
for i := 1; i <= 30; i++ {
server, _, err := client.GetByID(ctx, id)
if err != nil || server == nil {
return fmt.Errorf("failed to get info for server %q: %w", strconv.FormatInt(id, 10), err)
}
if server.Status == hcloud.ServerStatusOff {
break
}
time.Sleep(time.Second * 5)
}
_, err = client.Delete(ctx, server)
if err != nil {
return fmt.Errorf("failed to delete server %q: %w", strconv.FormatInt(id, 10), err)
}
return nil
}
func (c *hetznerCloudImplementation) DetachInstance(instance *cloudinstances.CloudInstance) error {
// Hetzner Cloud API doesn't provide the option of using cloud groups
return nil
}
// ProviderID returns the kOps API identifier for Hetzner Cloud
func (c *hetznerCloudImplementation) ProviderID() kops.CloudProviderID {
return kops.CloudProviderHetzner
}
// Region returns the Hetzner Cloud region
func (c *hetznerCloudImplementation) Region() string {
return c.regionView on GitHub (pinned to 4c8573c808)
Solutions
- Check if the server still exists; treat not_found as success and reconcile state
- Disable delete protection on the server (and its attached volumes/primary IPs) in Hetzner console or API
- Ensure the token has write permissions and no other process is deleting the server concurrently
- Retry the delete on transient errors
Example fix
// before
if err != nil {
return fmt.Errorf("failed to delete server %q: %w", strconv.FormatInt(id, 10), err)
}
// after
if err != nil {
if hcloud.IsError(err, hcloud.ErrorCodeNotFound) {
return nil // already deleted elsewhere
}
return fmt.Errorf("failed to delete server %q: %w", strconv.FormatInt(id, 10), err)
} Defensive patterns
Strategy: type-guard
Validate before calling
server, _, err := client.GetByID(ctx, id)
if server == nil {
return nil // already deleted; no Delete call needed
}
if server.Protection.Delete {
return fmt.Errorf("server %d has delete protection enabled", id)
} Type guard
var hcloudErr *hcloud.Error
if errors.As(err, &hcloudErr) && hcloudErr.Code == hcloud.ErrorCodeNotFound {
// treat as success: already deleted
} Try / catch
_, err = client.Delete(ctx, server)
if err != nil {
var hErr *hcloud.Error
if errors.As(err, &hErr) && hErr.Code == hcloud.ErrorCodeNotFound {
return nil
}
return fmt.Errorf("failed to delete server %q: %w", strconv.FormatInt(id, 10), err)
} Prevention
- Check server.Protection.Delete before deleting; disable protection first
- Treat not_found as successful deletion to stay idempotent
- Verify token write permissions
- Avoid concurrent deletions of the same server
When it happens
Trigger: Calling DeleteInstance/DeleteGroup when the server was already deleted (404 not_found), the token lacks delete permission, delete is blocked (e.g. protected server or attached primary IP/volume with delete protection), or transient API errors.
Common situations: Concurrent deletion by another kOps run or manual console deletion; servers with delete protection enabled; Hetzner API outages right after shutdown completes.
Related errors
- failed to list servers: %w
- failed to stop server %q: %w
- failed to get info for server %q: %w
- failed to list ssh keys: %w
- failed to list networks: %w
AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05).
Data as JSON: /api/errors/e783afe3873e3d27.
Report an issue: GitHub.