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
- Inspect the body value: remove or replace fields that are not JSON-serializable (chan, func, cycles, NaN/Inf)
- Add json tags to struct fields so the intended payload is marshaled correctly
- Give unserializable types a MarshalJSON method, or pre-convert them to a serializable representation before calling Request
- 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
- Use dedicated request DTO structs with json tags instead of ad-hoc map[string]interface{}
- Never put chan, func, or context values in a request payload
- Test each request type's marshaling in unit tests
- Keep float values finite (no NaN/Inf) in payloads
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 retriesView on GitHub (pinned to c245815e75)