kubernetes/kops · error

failed to stop server %q: %w

Error message

failed to stop server %q: %w

What it means

deleteServer issues a graceful Shutdown via the Hetzner API before deletion; this error is thrown when the Shutdown call itself returns an error from the Hetzner Cloud API. The server is left running and deletion aborts.

Source

Thrown at upup/pkg/fi/cloudup/hetzner/cloud.go:270

	serverID, err := strconv.ParseInt(instance.ID, 10, 64)
	if err != nil {
		return fmt.Errorf("failed to convert server ID %q to int: %w", instance.ID, err)
	}

	err = deleteServer(c, serverID)

	return err
}

// deleteServer shuts down and deletes the given server
func deleteServer(c *hetznerCloudImplementation, id int64) error {
	client := c.ServerClient()
	ctx := context.TODO()

	server := &hcloud.Server{ID: id}
	_, _, err := client.Shutdown(ctx, server)
	if err != nil {
		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)

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Check whether the server still exists (hcloud CLI / console); treat 404 as already deleted
  2. Verify the API token has write permission on servers
  3. Retry the deletion after transient errors; check for concurrent kOps processes
  4. Inspect the wrapped hcloud error code for the precise cause

Example fix

// before
if err != nil {
	return fmt.Errorf("failed to stop server %q: %w", strconv.FormatInt(id, 10), err)
}
// after
if err != nil {
	if hcloud.IsError(err, hcloud.ErrorCodeNotFound) {
		return nil // already deleted
	}
	return fmt.Errorf("failed to stop server %q: %w", strconv.FormatInt(id, 10), err)
}
Defensive patterns

Strategy: retry

Validate before calling

server, _, err := client.GetByID(ctx, id)
if server == nil {
	return nil // server already gone; skip shutdown/delete
}

Type guard

func serverExists(client *hcloud.ServerClient, id int64) bool {
	s, _, err := client.GetByID(context.TODO(), id)
	return err == nil && s != nil
}

Try / catch

err := deleteServer(c, id)
if err != nil {
	var hErr *hcloud.Error
	if errors.As(err, &hErr) && hErr.Code == hcloud.ErrorCodeNotFound {
		return nil
	}
	return err
}

Prevention

When it happens

Trigger: Calling DeleteInstance/DeleteGroup -> deleteServer when the server no longer exists (404 not_found), the API token lacks permission, the request times out, or the API is rate-limited/unavailable.

Common situations: Attempting to delete an already-removed server (e.g. manually deleted in Hetzner console or concurrent kOps run), token scope changes, or transient API failures during rolling updates.

Related errors


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