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
- Read the wrapped cause under this message for the precise failure (auth, name conflict, or file write)
- Pick a unique tunnel name or delete the existing tunnel via `cloudflared tunnel delete <name>`
- Re-authenticate with `cloudflared tunnel login` if the cause indicates credentials/cert issues
- 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
- Generate unique tunnel names (timestamp/suffix) in automation
- Verify cert.pem validity before create (token expiry causes auth failures)
- Pre-create a writable credentials directory or pass --cred-file explicitly
- Handle Cloudflare API 5xx with a bounded retry around create
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
- error parsing tunnel ID
- Expected to find a single tunnel with uuid %v but found %d t
- ErrTunnelNameConflict
- ErrNoTunnelID
- ErrInvalidTunnelID
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/d3221b0bd64ace57.
Report an issue: GitHub.