gastownhall/beads · error

marshal request body: %w

Error message

marshal request body: %w

What it means

doRequest could not JSON-serialize the request payload passed as requestBody. The error wraps the json.Marshal error, so the underlying cause (unsupported type, channel/func field, invalid value) is in the chain. It fires only when requestBody != nil.

Source

Thrown at internal/notion/client.go:260

}

func (c *Client) doRequest(ctx context.Context, method, path string, requestBody interface{}) ([]byte, error) {
	if c == nil {
		return nil, fmt.Errorf("notion client is nil")
	}
	if strings.TrimSpace(c.Token) == "" {
		return nil, fmt.Errorf("Notion token not configured")
	}
	httpClient := c.HTTPClient
	if httpClient == nil {
		httpClient = &http.Client{Timeout: DefaultTimeout}
	}

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

	requestURL := path
	if !strings.HasPrefix(requestURL, "http://") && !strings.HasPrefix(requestURL, "https://") {
		requestURL = strings.TrimSuffix(c.BaseURL, "/") + path
	}
	req, err := http.NewRequestWithContext(ctx, method, requestURL, bodyReader)
	if err != nil {
		return nil, fmt.Errorf("create request: %w", err)
	}
	req.Header.Set("Authorization", "Bearer "+c.Token)
	req.Header.Set("Notion-Version", c.NotionVersion)
	req.Header.Set("Accept", "application/json")
	if requestBody != nil {
		req.Header.Set("Content-Type", "application/json")
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error (%w chain) to identify the offending field/type reported by encoding/json.
  2. Ensure requestBody is a plain JSON-serializable struct/map with exported fields and proper json tags.
  3. Marshal it yourself first in a test: b, err := json.Marshal(req); check err to reproduce locally.
  4. Set the field to omitempty or remove unsupported fields from the request type.

Example fix

// before
type req struct {
    Callback func() `json:"callback"`
}
client.Query(ctx, req{Callback: fn}) // marshal error

// after
type req struct {
    Name string `json:"name"`
}
client.Query(ctx, req{Name: "x"})
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

if err != nil && strings.Contains(err.Error(), "marshal request body") {
    log.Printf("request body not JSON-serializable: %v", err)
}

Prevention

When it happens

Trigger: Passing a requestBody containing values json cannot encode: a struct field of type chan, func, or complex; an unexported-fields-only struct (marshals but often fine) or types with custom MarshalJSON that return errors; passing a map with non-string keys of an unsupported kind.

Common situations: Users build the request struct with a field typed as interface{} holding a channel or func; copy-pasted types with json:"-"-style custom marshaling gone wrong; passing *time.Time wrappers with bad MarshalJSON implementations.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/63aebdd0bddcacfc. Report an issue: GitHub.