cloudflare/cloudflared · warning

400 Bad Request

Error message

400 Bad Request

What it means

This is the fallback plain-text response written by writeHTTPErrorResponse in the management middleware when encoding the structured JSON error body fails. Because the HTTP status header (400) has already been written, the middleware can only emit a basic '400 Bad Request' text body. It signals a client sent a request the management API could not accept AND that the error payload itself could not be serialized.

Source

Thrown at management/middleware.go:63

// Middleware validation error HTTP response JSON for returning to the eyeball
type managementErrorResponse struct {
	Success bool              `json:"success,omitempty"`
	Errors  []managementError `json:"errors,omitempty"`
}

// writeErrorResponse will respond to the eyeball with basic HTTP JSON payloads with validation failure information
func writeHTTPErrorResponse(w http.ResponseWriter, errResp managementError) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusBadRequest)
	err := json.NewEncoder(w).Encode(managementErrorResponse{
		Success: false,
		Errors:  []managementError{errResp},
	})
	// we have already written the header, so write a basic error response if unable to encode the error
	if err != nil {
		// fallback to text message
		http.Error(w, fmt.Sprintf(
			"%d %s",
			http.StatusBadRequest,
			http.StatusText(http.StatusBadRequest),
		), http.StatusBadRequest)
	}
}

View on GitHub (pinned to 2253eeeb25)

Solutions

  1. Inspect the request body/parameters sent to the management API and correct them (valid JSON, required fields present)
  2. Set Content-Type: application/json and send a well-formed management protocol message
  3. Use an up-to-date cloudflared client so request/response schemas match the running daemon version
  4. Check cloudflared logs for the original error that triggered the 400 to identify the rejected field

Example fix

// before: malformed management request
{"unknown_field": true}
// after
{"type": "grpc-request", "body": {"request_type": "configuration", "version": {}}}
{"capabilities": [], "version": "2023.4.1"}
Defensive patterns

Strategy: validation

Validate before calling

body, _ := json.Marshal(managementReq)
if !json.Valid(body) {
	return errors.New("management request payload is not valid JSON")
}
req.Header.Set("Content-Type", "application/json")

Try / catch

resp, err := http.Post(managementURL, "application/json", bytes.NewReader(body))
if err != nil {
	return err
}
if resp.StatusCode == http.StatusBadRequest {
	var mgmtResp struct{ Errors []struct{ Code int; Message string } `json:"errors"` }
	json.NewDecoder(resp.Body).Decode(&mgmtResp)
	return fmt.Errorf("management API rejected request: %+v", mgmtResp.Errors)
}

Prevention

When it happens

Trigger: A client sends an invalid request to the cloudflared management API (bad JSON body or invalid query parameters); the handler writes a 400 header, and then json encoding of the managementError response fails, triggering the http.Error fallback with the literal '400 Bad Request' text.

Common situations: Programmatic clients POSTing malformed JSON to the management endpoint; missing required fields in management request payloads; tooling hitting the wrong endpoint shape; handcrafted curl requests with wrong content type.

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/4c210bda6819ba8d. Report an issue: GitHub.