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

  1. List existing tunnels first and reuse the existing tunnel ID instead of creating a new one
  2. Choose a unique tunnel name (e.g. include an environment or run identifier)
  3. Delete the stale tunnel before recreating, or use `cloudflared tunnel cleanup`
  4. 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

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


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