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 errorView on GitHub (pinned to 2253eeeb25)
Solutions
- Retry with `cloudflared tunnel delete --force <id>` if active connections or bound resources block deletion
- Check the tunnel's state with `cloudflared tunnel info <id>` and clean up connections/routes first
- Verify origin cert credentials have permission to delete tunnels (`cloudflared tunnel login` again if stale)
- Retry after a transient API outage (check https://www.cloudflarestatus.com)
- 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
- Use --force when the tunnel may still have live connections
- Ensure the cert's service token has tunnel:delete scope
- Retry deletes on 5xx with exponential backoff
- Verify with `tunnel info` that no active connectors remain before deleting
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
- Can't get tunnel information. Please check tunnel id: %s
- Invalid CIDR supplied for %s
- invalid connection override: %s
- failed to create account level endpoint
- failed to create route account-level endpoint
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/0416964dd20c4b69.
Report an issue: GitHub.