cloudflare/cloudflared · error

tunnel name required

Error message

tunnel name required

What it means

CreateTunnel rejects an empty name string before making any API call, returning this client-side validation error. Tunnel names are required by the Cloudflare API and cloudflared validates locally to fail fast. No network request is made when this error occurs.

Solutions

  1. Provide a non-empty tunnel name to CreateTunnel
  2. Check the config/env source of the name for typos or unset variables
  3. Add caller-side validation with a clear message before invoking the client

Example fix

// before
name := os.Getenv("TUNNEL_NAME")
tunnel, err := client.CreateTunnel(name, secret)
// after
name := os.Getenv("TUNNEL_NAME")
if name == "" {
    return errors.New("TUNNEL_NAME must be set to a non-empty tunnel name")
}
tunnel, err := client.CreateTunnel(name, secret)
Defensive patterns

Strategy: validation

Validate before calling

if name == "" {
    return errors.New("tunnel name must be non-empty before calling CreateTunnel")
}

Try / catch

t, err := client.CreateTunnel(name, secret)
if err != nil && strings.Contains(err.Error(), "tunnel name required") {
    return fmt.Errorf("invalid tunnel config: name is empty: %w", err)
}

Prevention

When it happens

Trigger: Calling RESTClient.CreateTunnel with "" as the name — typically from an unset config value, empty environment variable, or CLI flag that defaulted to empty.

Common situations: Config files missing the tunnel name field; shell variables expanding to empty (e.g. $TUNNEL_NAME unset in CI); scripting errors passing the wrong positional argument.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at cfapi/tunnel.go:94

}

func NewCleanupParams() *CleanupParams {
	return &CleanupParams{
		queryParams: url.Values{},
	}
}

func (cp *CleanupParams) ForClient(clientID uuid.UUID) {
	cp.queryParams.Set("client_id", clientID.String())
}

func (cp CleanupParams) encode() string {
	return cp.queryParams.Encode()
}

func (r *RESTClient) CreateTunnel(name string, tunnelSecret []byte) (*TunnelWithToken, error) {
	if name == "" {
		return nil, errors.New("tunnel name required")
	}
	if _, err := uuid.Parse(name); err == nil {
		return nil, errors.New("you cannot use UUIDs as tunnel names")
	}
	body := &newTunnel{
		Name:         name,
		TunnelSecret: tunnelSecret,
	}

	resp, err := r.sendRequest("POST", r.baseEndpoints.accountLevel, body)
	if err != nil {
		return nil, errors.Wrap(err, "REST request failed")
	}
	defer resp.Body.Close()

	switch resp.StatusCode {
	case http.StatusOK:
		var tunnel TunnelWithToken

View on GitHub (pinned to 2253eeeb25)