cloudflare/cloudflared · error

there should only be 1 non-deleted Tunnel named %s

Error message

there should only be 1 non-deleted Tunnel named %s

What it means

findIDs resolves multiple tunnel names to UUIDs using a single Tunnelstore API call per non-UUID input. The API contract assumes a tunnel name maps to exactly one non-deleted tunnel; when the filtered list returns a count other than 1, cloudflared cannot disambiguate and raises this error.

Source

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

}

// findIDs is just like mapping `findID` over a slice, but it only uses
// one Tunnelstore API call per non-UUID input provided.
func (sc *subcommandContext) findIDs(inputs []string) ([]uuid.UUID, error) {
	uuids, names := splitUuids(inputs)

	for _, name := range names {
		filter := cfapi.NewTunnelFilter()
		filter.NoDeleted()
		filter.ByName(name)

		tunnels, err := sc.list(filter)
		if err != nil {
			return nil, err
		}

		if len(tunnels) != 1 {
			return nil, fmt.Errorf("there should only be 1 non-deleted Tunnel named %s", name)
		}

		uuids = append(uuids, tunnels[0].ID)
	}

	return uuids, nil
}

func splitUuids(inputs []string) ([]uuid.UUID, []string) {
	uuids := make([]uuid.UUID, 0)
	names := make([]string, 0)

	for _, input := range inputs {
		id, err := uuid.Parse(input)
		if err != nil {
			names = append(names, input)
		} else {
			uuids = append(uuids, id)

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Run `cloudflared tunnel list` and resolve duplicates: delete or rename extra tunnels with the same name.
  2. If zero results, check spelling and account (cert.pem must belong to the account owning the tunnel).
  3. Use the tunnel's UUID directly instead of the name to avoid ambiguity.

Example fix

// before
cloudflared tunnel delete staging   // ambiguous or missing
// after
cloudflared tunnel list
cloudflared tunnel delete <exact-tunnel-uuid>
Defensive patterns

Strategy: validation

Validate before calling

out, _ := exec.Command("cloudflared", "tunnel", "list", "--output", "json").Output()
var tunnels []struct{ Name string }
json.Unmarshal(out, &tunnels)
count := 0
for _, t := range tunnels { if t.Name == name { count++ } }
if count != 1 { return fmt.Errorf("expected exactly 1 tunnel named %s, found %d", name, count) }

Prevention

When it happens

Trigger: Calling commands that accept multiple tunnel names (e.g. `cloudflared tunnel delete <name1> <name2>` or cleanup) where a name matches zero or more than one non-deleted tunnel in the account.

Common situations: Duplicate tunnels created under the same name in different environments sharing one account; the name was already deleted (list returns 0); race where a duplicate tunnel was created concurrently.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


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