cloudflare/cloudflared · error

Error deleting tunnel %s

Error message

Error deleting tunnel %s

What it means

cloudflared wraps a failure from `client.DeleteTunnel(tunnel.ID, forceFlagSet)` — the tunnel was found (GetTunnel succeeded and it is not already deleted) but the actual DELETE request against the Tunnel Store API failed. This is an API-level rejection (permissions, non-deleted tunnel resources, server error), not a lookup failure.

Source

Thrown at cmd/cloudflared/tunnel/subcommand_context.go:231

	client, err := sc.client()
	if err != nil {
		return err
	}

	for _, id := range tunnelIDs {
		tunnel, err := client.GetTunnel(id)
		if err != nil {
			return errors.Wrapf(err, "Can't get tunnel information. Please check tunnel id: %s", id)
		}

		// Check if tunnel DeletedAt field has already been set
		if !tunnel.DeletedAt.IsZero() {
			return fmt.Errorf("Tunnel %s has already been deleted", tunnel.ID)
		}

		if err := client.DeleteTunnel(tunnel.ID, forceFlagSet); err != nil {
			return errors.Wrapf(err, "Error deleting tunnel %s", tunnel.ID)
		}

		credFinder := sc.credentialFinder(id)
		if tunnelCredentialsPath, err := credFinder.Path(); err == nil {
			if err = os.Remove(tunnelCredentialsPath); err != nil {
				sc.log.Info().Msgf("Tunnel %v was deleted, but we could not remove its credentials file  %s: %s. Consider deleting this file manually.", id, tunnelCredentialsPath, err)
			}
		}
	}
	return nil
}

// findCredentials will choose the right way to find the credentials file, find it,
// and add the TunnelID into any old credentials (generated before TUN-3581 added the `TunnelID`
// field to credentials files)
func (sc *subcommandContext) findCredentials(tunnelID uuid.UUID) (connection.Credentials, error) {
	var credentials connection.Credentials
	var err error

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Retry with `cloudflared tunnel delete --force <id>` if active connections or bound resources block deletion
  2. Check the tunnel's state with `cloudflared tunnel info <id>` and clean up connections/routes first
  3. Verify origin cert credentials have permission to delete tunnels (`cloudflared tunnel login` again if stale)
  4. Retry after a transient API outage (check https://www.cloudflarestatus.com)
  5. Inspect the wrapped error text for the specific API status code and act on it

Example fix

// before
if err := client.DeleteTunnel(tunnel.ID, forceFlagSet); err != nil {
    return errors.Wrapf(err, "Error deleting tunnel %s", tunnel.ID)
}
// after (callers run: cloudflared tunnel delete --force <id>)
// CLI side:
//   cloudflared tunnel delete --force <id>
// or in code, surface the force hint:
if err := client.DeleteTunnel(tunnel.ID, forceFlagSet); err != nil {
    return errors.Wrapf(err, "Error deleting tunnel %s (try --force if connections are still active)", tunnel.ID)
}
Defensive patterns

Strategy: retry

Validate before calling

info, err := exec.Command("cloudflared", "tunnel", "info", tunnelID).Output()
if err != nil { /* tunnel unreachable/unknown */ }
// decide if --force is needed based on active connections in info output

Try / catch

err := run("cloudflared", "tunnel", "delete", tunnelID)
if err != nil && strings.Contains(err.Error(), "Error deleting tunnel") {
    // retry with --force or after backoff
    _ = run("cloudflared", "tunnel", "delete", "--force", tunnelID)
}

Prevention

When it happens

Trigger: `cloudflared tunnel delete <id>` where the API DELETE returns an error: token/cert lacks delete permission, tunnel still has active connections or route bindings without --force, or a 5xx from the API.

Common situations: Deleting a tunnel that still has remnant connections; using a cert.pem whose service token lacks tunnel delete scope; transient Cloudflare API outage; calling delete with --force missing when connections are still registered.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06). Data as JSON: /api/errors/0416964dd20c4b69. Report an issue: GitHub.