cloudflare/cloudflared · error

failed to serialize json body

Error message

failed to serialize json body

What it means

RESTClient.sendRequest marshals the request body to JSON before sending it to the Cloudflare API and wraps json.Marshal failures with this message. It means the Go struct passed as the request body could not be serialized, which for well-formed API request types is nearly impossible — it usually indicates a type that JSON cannot represent (channels, funcs, cycles) or a custom MarshalJSON returning an error.

Source

Thrown at cfapi/base_client.go:91

			zoneLevel:     *zoneLevelEndpoint,
			accountRoutes: *accountRoutesEndpoint,
			accountVnets:  *accountVnetsEndpoint,
		},
		authToken: authToken,
		userAgent: userAgent,
		client: http.Client{
			Transport: &httpTransport,
			Timeout:   defaultTimeout,
		},
		log: log,
	}, nil
}

func (r *RESTClient) sendRequest(method string, url url.URL, body interface{}) (*http.Response, error) {
	var bodyReader io.Reader
	if body != nil {
		if bodyBytes, err := json.Marshal(body); err != nil {
			return nil, errors.Wrap(err, "failed to serialize json body")
		} else {
			bodyReader = bytes.NewBuffer(bodyBytes)
		}
	}

	req, err := http.NewRequest(method, url.String(), bodyReader)
	if err != nil {
		return nil, errors.Wrapf(err, "can't create %s request", method)
	}
	req.Header.Set("User-Agent", r.userAgent)
	if bodyReader != nil {
		req.Header.Set("Content-Type", jsonContentType)
	}
	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", r.authToken))
	req.Header.Add("Accept", "application/json;version=1")
	return r.client.Do(req)
}

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Inspect the body type passed to the API call; remove unsupported fields (chan, func, circular references).
  2. Test json.Marshal(body) directly to see the underlying error.
  3. Use the library's defined request structs (e.g. cfapi.Route, CreateTunnelParams-derived types) rather than ad-hoc maps.
  4. Fix any custom MarshalJSON implementations that return errors.

Example fix

// before
client.CreateTunnel(t, &Tunnel{Conn: make(chan int)})
// after
client.CreateTunnel(t, &cfapi.Tunnel{Name: name, Secret: secret})
Defensive patterns

Strategy: validation

Validate before calling

if _, err := json.Marshal(body); err != nil {
	return fmt.Errorf("request body not serializable: %w", err)
}

Try / catch

if err := client.CreateTunnel(t, params); err != nil {
	if strings.Contains(err.Error(), "failed to serialize json body") {
		return fmt.Errorf("bad request body type %T: %w", params, err)
	}
	return err
}

Prevention

When it happens

Trigger: Passing a body containing unsupported types (chan, func, cyclic pointers) or a type whose custom MarshalJSON errors when calling CreateTunnel, AddRoute, DeleteRoute, GetByIP, etc.

Common situations: Constructing route/tunnel request structs with unusual field types or embedding non-serializable values; typos leading to interface{} values holding unserializable data.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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