AlexxIT/go2rtc · error

failed to marshal request body

Error message

failed to marshal request body: %w

What it means

RingApi.Request marshals the caller-supplied 'body' argument to JSON before sending. If json.Marshal fails on that value, the request is never sent and this error is returned. It indicates the request payload contains data that cannot be serialized to JSON (unsupported types, cycles, invalid values).

Solutions

  1. Inspect the body value: remove or replace fields that are not JSON-serializable (chan, func, cycles, NaN/Inf)
  2. Add json tags to struct fields so the intended payload is marshaled correctly
  3. Give unserializable types a MarshalJSON method, or pre-convert them to a serializable representation before calling Request
  4. Pass bytes.NewBuffer/[]byte of pre-marshaled JSON if you must control encoding yourself

Example fix

// before
c.Request("POST", url, map[string]interface{}{"callback": someFunc}) // marshal fails
// after
c.Request("POST", url, struct {
    DeviceID string `json:"device_id"`
}{DeviceID: id})
Defensive patterns

Strategy: validation

Validate before calling

// Go: verify the body serializes before the call
func assertSerializable(v interface{}) error {
    _, err := json.Marshal(v)
    return err // call before api.Request
}

Try / catch

// Go
if err := assertSerializable(body); err != nil {
    return fmt.Errorf("bad request payload: %w", err)
}
resp, err := api.Request(method, url, body)
if err != nil && strings.Contains(err.Error(), "failed to marshal request body") {
    return fmt.Errorf("request body not JSON-serializable: %w", err)
}

Prevention

When it happens

Trigger: Passing a body to Request containing unmarshalable values: channels, funcs, cyclic pointers, NaN/Inf floats, or a custom type with a MarshalJSON method that returns an error.

Common situations: Accidentally passing a Go struct with unexported/unsupported fields, embedding an io.Reader or func field, or passing non-serializable runtime values (e.g., a context or logger) as the body instead of the intended DTO.

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 AlexxIT/go2rtc@c245815e75 (2026-09-07). Data as JSON: /api/errors/f8e5e9b2c650adbb. Report an issue: GitHub.

Appendix: source

Thrown at pkg/ring/api.go:413

	var ticket SocketTicketResponse
	if err := json.Unmarshal(response, &ticket); err != nil {
		return nil, fmt.Errorf("failed to unmarshal socket ticket response: %w", err)
	}

	return &ticket, nil
}

func (c *RingApi) Request(method, url string, body interface{}) ([]byte, error) {
	// Ensure we have a valid session
	if err := c.ensureSession(); err != nil {
		return nil, fmt.Errorf("session validation failed: %w", err)
	}

	var bodyReader io.Reader
	if body != nil {
		jsonBody, err := json.Marshal(body)
		if err != nil {
			return nil, fmt.Errorf("failed to marshal request body: %w", err)
		}
		bodyReader = bytes.NewReader(jsonBody)
	}

	// Create request
	req, err := http.NewRequest(method, url, bodyReader)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	// Set headers
	req.Header.Set("Authorization", "Bearer "+c.authToken.AccessToken)
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("hardware_id", c.hardwareID)
	req.Header.Set("User-Agent", "android:com.ringapp")

	// Make request with retries

View on GitHub (pinned to c245815e75)