cloudflare/cloudflared · error

quick tunnel provisioning failed with status %d: %s

Error message

quick tunnel provisioning failed with status %d: %s

What it means

RunQuickTunnel provisions a free trycloudflare.com quick tunnel via an HTTP API. When the response status code is outside 2xx and the response body parses as a QuickTunnelResponse containing structured errors, cloudflared surfaces the status code plus the formatted API errors. This is the API's structured error path.

Source

Thrown at cmd/cloudflared/tunnel/quick_tunnel.go:86

	req.Header.Add("Content-Type", "application/json")
	req.Header.Add("User-Agent", buildInfo.UserAgent())

	resp, err := client.Do(req)
	if err != nil {
		return errors.Wrap(err, "failed to request quick Tunnel")
	}
	defer func() { _ = resp.Body.Close() }()

	// This will read the entire response into memory so we can print it in case of error
	respBody, err := io.ReadAll(resp.Body)
	if err != nil {
		return errors.Wrap(err, "failed to read quick-tunnel response")
	}

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		var data QuickTunnelResponse
		if err := json.Unmarshal(respBody, &data); err == nil && len(data.Errors) > 0 {
			return fmt.Errorf("quick tunnel provisioning failed with status %d: %s", resp.StatusCode, formatQuickTunnelErrors(data.Errors))
		}
		return fmt.Errorf("quick tunnel provisioning failed with status %d: %s", resp.StatusCode, string(respBody))
	}

	var data QuickTunnelResponse
	if err := json.Unmarshal(respBody, &data); err != nil {
		respString := string(respBody)
		fields := map[string]interface{}{"status_code": resp.Status}
		sc.log.Err(err).Fields(fields).Msgf("Error unmarshaling QuickTunnel response: %s", respString)
		return errors.Wrap(err, "failed to unmarshal quick Tunnel")
	}

	// TODO(TUN-10791): Add CLI-level coverage that provisioning errors are logged to users.
	if len(data.Errors) > 0 {
		return fmt.Errorf("quick tunnel provisioning failed: %s", formatQuickTunnelErrors(data.Errors))
	}

	if !data.Success {

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Read the formatted API errors in the message and address the specific cause (e.g. wait out a rate limit)
  2. Retry after a short delay — quick-tunnel provisioning is a free shared service and transient 5xx/429s are common
  3. Check the Cloudflare status page for ongoing incidents; use a named tunnel instead if you need guaranteed availability
  4. Verify no corporate proxy/TLS interception is altering the request to the provisioning API
Defensive patterns

Strategy: retry

Validate before calling

resp, err := http.Get(quickTunnelEndpoint)
if err == nil && resp.StatusCode >= 400 {
    return fmt.Errorf("quick tunnel API unhealthy (status %d); use a named tunnel", resp.StatusCode)
}

Type guard

func is2xx(code int) bool { return code >= 200 && code < 300 }

Try / catch

tunnel, err := RunQuickTunnel(c)
if err != nil {
    log.Error().Err(err).Msg("quick tunnel provisioning failed; retrying with backoff")
    // retry with exponential backoff, or fall back to named tunnel
}

Prevention

When it happens

Trigger: The quick-tunnel provisioning endpoint (api.trycloudflare.com) returns a 4xx/5xx with a JSON body whose `errors` array is non-empty — e.g. rate limiting, service outage, or rejected requests.

Common situations: Running `cloudflared tunnel --url localhost:8080` behind a corporate proxy that intercepts the request; Cloudflare API incidents; too many quick tunnels from one IP hitting rate limits.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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