cloudflare/cloudflared · error
failed to unmarshal quick Tunnel
Error message
failed to unmarshal quick Tunnel
What it means
The quick-tunnel service returned a body that could not be parsed as JSON (QuickTunnelResponse). cloudflared logs the raw body and status, then returns this wrapped error. It means the API replied something other than the expected JSON schema.
Source
Thrown at cmd/cloudflared/tunnel/quick_tunnel.go:96
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 {
return errors.New("quick tunnel provisioning failed")
}
tunnelID, err := uuid.Parse(data.Result.ID)
if err != nil {
return errors.Wrap(err, "failed to parse quick Tunnel ID")
}
credentials := connection.Credentials{
AccountTag: data.Result.AccountTag,View on GitHub (pinned to 2253eeeb25)
Solutions
- Check the logged 'Error unmarshaling QuickTunnel response' line to see the raw body (often an HTML captive-portal page)
- Complete captive-portal/Wi-Fi login or bypass the intercepting proxy and retry
- Upgrade cloudflared to the latest version to match current API response schema
- If using a custom quick-service, verify it returns the Cloudflare QuickTunnelResponse JSON shape
Defensive patterns
Strategy: validation
Validate before calling
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
return fmt.Errorf("quick-tunnel returned non-JSON content type %q (captive portal/proxy?)", ct)
} Try / catch
if err := json.Unmarshal(respBody, &data); err != nil {
log.Printf("quick-tunnel non-JSON response (%d): %s", resp.StatusCode, string(respBody))
return err
} Prevention
- Complete captive-portal logins before running
- Exclude cloudflared endpoints from TLS-intercepting proxies
- Pin a supported cloudflared version
When it happens
Trigger: json.Unmarshal(respBody, &data) fails after a successful HTTP response from the quick-service endpoint.
Common situations: Captive portals or proxies returning HTML error/login pages instead of JSON; an outdated cloudflared talking to a changed API; custom quick-service endpoints that return different payloads.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- Create Tunnel API call failed
- ErrUnauthorized
- ErrBadRequest
- ErrAPINoSuccess
- Failed to fetch page. Server returned: %d
AI-assisted analysis of cloudflare/cloudflared@2253eeeb25 (2026-09-06).
Data as JSON: /api/errors/20f27cc6642ff616.
Report an issue: GitHub.