gastownhall/beads · error
failed to marshal request body: %w
Error message
failed to marshal request body: %w
What it means
doRequest serializes the request body to JSON before sending. If the supplied body struct cannot be marshaled (unsupported types like channels, funcs, or cyclic references), the request is aborted locally and this wrapped error is returned. In practice it is nearly impossible to trigger because bodies are plain structs.
Source
Thrown at internal/gitlab/client.go:106
if len(params) > 0 {
values := url.Values{}
for k, v := range params {
values.Set(k, v)
}
u += "?" + values.Encode()
}
return u
}
// doRequest performs an HTTP request with authentication and retry logic.
func (c *Client) doRequest(ctx context.Context, method, urlStr string, body interface{}) ([]byte, http.Header, error) {
var reqBody io.Reader
if body != nil {
jsonBody, err := json.Marshal(body)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal request body: %w", err)
}
reqBody = bytes.NewReader(jsonBody)
}
var lastErr error
for attempt := 0; attempt <= MaxRetries; attempt++ {
// Reset body reader at top of loop so retries after network errors
// don't send empty bodies (the reader may be at EOF).
if body != nil {
jsonBody, err := json.Marshal(body)
if err != nil {
return nil, nil, fmt.Errorf("failed to marshal request body: %w", err)
}
reqBody = bytes.NewReader(jsonBody)
}
req, err := http.NewRequestWithContext(ctx, method, urlStr, reqBody)
if err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the body struct for channel/func/cyclic fields and remove or tag them `json:"-"`
- Ensure all body fields are JSON-serializable types (string, int, slices, maps, pointers)
- If passing a custom body, validate with json.Marshal in a unit test before the real call
- Update to the latest version in case a field type was changed upstream
Example fix
// before
type update struct{ Hook func() `json:"hook"` }
client.UpdateIssue(ctx, 42, update{Hook: fn}) // marshal fails
// after
type update struct{ Hook func() `json:"-"` }
client.UpdateIssue(ctx, 42, update{Hook: fn}) Defensive patterns
Strategy: validation
Validate before calling
if _, err := json.Marshal(body); err != nil {
return fmt.Errorf("body not JSON-marshalable: %w", err)
} Type guard
func jsonSafe(v interface{}) bool {
switch v.(type) { case chan struct{}, func(): return false }
return json.NewMarshalableCheck(v) == nil // or: json.Marshal(v) == nil error
} Try / catch
if err != nil && strings.Contains(err.Error(), "failed to marshal request body") {
log.Fatalf("programmer error: request body is not JSON-serializable: %v", err)
} Prevention
- Keep request body structs to plain JSON types; tag non-serializable fields with `json:"-"`
- Add a test that marshals every client request struct
- Avoid embedding channels, funcs, or self-referencing pointers in payload types
When it happens
Trigger: Calling any Client method (CreateIssue, UpdateIssue, etc.) with a body containing fields that encoding/json cannot marshal: channels, funcs, cycles, or invalid types in custom struct fields passed via extension points.
Common situations: Forking/monkey-patching the client with a body struct containing unexported-but-problematic types; embedding a sync.Mutex by value is fine, but embedding a channel or a func-typed field without json tags pointing at it; hand-building request bodies in tests.
Related errors
- failed to marshal backup state: %w
- failed to marshal issue %s: %w
- failed to write JSON: %w
- marshaling Linear milestone metadata: %w
- failed to marshal interactions log entry: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ea2e241b06cedffe.
Report an issue: GitHub.