cloudflare/cloudflared · error

failed to create tunnel

Error message

failed to create tunnel

What it means

`cloudflared tunnel create` calls sc.create(name, credFile, secret) which talks to the Cloudflare API to provision the tunnel and then writes the credentials file. Any failure along that path is wrapped as 'failed to create tunnel'. This groups authentication errors, name conflicts, and local file write failures.

Source

Thrown at cmd/cloudflared/tunnel/subcommands.go:290

	return randomBytes, err
}

func createCommand(c *cli.Context) error {
	sc, err := newSubcommandContext(c)
	if err != nil {
		return errors.Wrap(err, "error setting up logger")
	}

	if c.NArg() != 1 {
		return cliutil.UsageError(`"cloudflared tunnel create" requires exactly 1 argument, the name of tunnel to create.`)
	}
	name := c.Args().First()

	warningChecker := updater.StartWarningCheck(c)
	defer warningChecker.LogWarningIfAny(sc.log)

	_, err = sc.create(name, c.String(CredFileFlag), c.String(createSecretFlag.Name))
	return errors.Wrap(err, "failed to create tunnel")
}

func tunnelFilePath(tunnelID uuid.UUID, directory string) (string, error) {
	fileName := fmt.Sprintf("%v.json", tunnelID)
	filePath := filepath.Clean(fmt.Sprintf("%s/%s", directory, fileName))
	return homedir.Expand(filePath)
}

// writeTunnelCredentials saves `credentials` as a JSON into `filePath`, only if
// the file does not exist already
func writeTunnelCredentials(filePath string, credentials *connection.Credentials) error {
	if _, err := os.Stat(filePath); !os.IsNotExist(err) {
		if err == nil {
			return fmt.Errorf("%s already exists", filePath)
		}
		return err
	}
	body, err := json.Marshal(credentials)

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Read the wrapped cause under this message for the precise failure (auth, name conflict, or file write)
  2. Pick a unique tunnel name or delete the existing tunnel via `cloudflared tunnel delete <name>`
  3. Re-authenticate with `cloudflared tunnel login` if the cause indicates credentials/cert issues
  4. Ensure the credentials output directory exists and is writable (or pass --cred-file to a writable path)

Example fix

// before
cloudflared tunnel create my-tunnel   # name already exists
// after
cloudflared tunnel list && cloudflared tunnel create my-tunnel-2   # unique name
Defensive patterns

Strategy: try-catch

Validate before calling

existing, _ := cloudflared("tunnel", "list", "--output", "json")
if strings.Contains(existing, "\"name\": \""+name+"\"") {
	return fmt.Errorf("tunnel %q already exists", name)
}

Try / catch

if _, err := sc.create(name, credFile, secret); err != nil {
	var conflict bool
	if strings.Contains(err.Error(), "already exists") { conflict = true }
	return fmt.Errorf("failed to create tunnel: %w", err)
}

Prevention

When it happens

Trigger: Running `cloudflared tunnel create <name>` when: the name already exists in the account, the API client is unauthenticated (no cert.pem), the Cloudflare API returns a non-2xx response, or the credentials JSON cannot be written to the --cred-file location.

Common situations: Re-running create with a tunnel name that already exists; expired or missing origin certificate; running in a read-only home directory so the <tunnelID>.json cannot be written; API outages or 5xx responses.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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