cloudflare/cloudflared · error
ErrTunnelNameConflict
ErrTunnelNameConflict
Error message
tunnel with name already exists
What it means
ErrTunnelNameConflict is returned by CreateTunnel when the Cloudflare API responds with HTTP 409 Conflict, meaning a tunnel with the same name already exists in the account. Tunnel names must be unique per account, so creating a duplicate is rejected. It is a package-level sentinel usable with errors.Is.
Source
Thrown at cfapi/tunnel.go:16
package cfapi
import (
"fmt"
"io"
"net"
"net/http"
"net/url"
"path"
"time"
"github.com/google/uuid"
"github.com/pkg/errors"
)
var ErrTunnelNameConflict = errors.New("tunnel with name already exists")
type ManagementResource int
const (
Logs ManagementResource = iota
Admin
HostDetails
)
func (r ManagementResource) String() string {
switch r {
case Logs:
return "logs"
case Admin:
return "admin"
case HostDetails:
return "host_details"
default:View on GitHub (pinned to 2253eeeb25)
Solutions
- List existing tunnels first and reuse the existing tunnel ID instead of creating a new one
- Choose a unique tunnel name (e.g. include an environment or run identifier)
- Delete the stale tunnel before recreating, or use `cloudflared tunnel cleanup`
- Handle the sentinel with errors.Is(err, cfapi.ErrTunnelNameConflict) and treat as success if the existing tunnel is the intended one
Example fix
// before
tunnel, err := client.CreateTunnel(name, secret)
if err != nil {
return err
}
// after
tunnel, err := client.CreateTunnel(name, secret)
if errors.Is(err, cfapi.ErrTunnelNameConflict) {
tunnel, err = client.GetTunnelByName(ctx, accountTag, name)
if err != nil {
return err
}
} else if err != nil {
return err
} Defensive patterns
Strategy: validation
Validate before calling
names, err := client.ListTunnelNames(ctx, accountTag)
if err != nil { return err }
for _, n := range names {
if n == desiredName { /* reuse existing tunnel */ }
} Type guard
func isNameConflict(err error) bool { return errors.Is(err, cfapi.ErrTunnelNameConflict) } Try / catch
t, err := client.CreateTunnel(name, secret)
if errors.Is(err, cfapi.ErrTunnelNameConflict) {
t, err = client.GetTunnelByName(ctx, accountTag, name) // fall back to existing
} Prevention
- Check for an existing tunnel with the same name before creating
- Make provisioning scripts idempotent (create-or-get)
- Namespace tunnel names per environment/pipeline run
- Serialize creation steps to avoid two jobs racing
When it happens
Trigger: Calling RESTClient.CreateTunnel with a name that already exists for another tunnel in the same Cloudflare account, or re-running a provisioning script without deleting the previously created tunnel.
Common situations: Idempotency mistakes in CI/CD tunnel provisioning; leftover tunnels from failed deployments; two pipeline jobs racing to create the same named tunnel; migrating scripts that assume fresh accounts.
Related errors
- failed to get checksums: {0}
- failed to upload checksum: {0}
- ErrUnauthorized
- ErrBadRequest
- ErrAPINoSuccess
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/d87158c839061a39.
Report an issue: GitHub.