chenhg5/cc-connect · error
reasonix: marshal body: %w
Error message
reasonix: marshal body: %w
What it means
httpPost marshals the request body to JSON before POSTing to the reasonix serve endpoint. If json.Marshal fails, it wraps the error as 'reasonix: marshal body: %w'. This should be nearly impossible for plain config structs but can occur for bodies containing channels, funcs, or cyclic references.
Source
Thrown at agent/reasonix/session.go:479
func (s *reasonixSession) flushThinking() {
s.mu.Lock()
text := s.thinkingBuf.String()
s.thinkingBuf.Reset()
s.mu.Unlock()
if text == "" {
return
}
s.emit(core.Event{Type: core.EventThinking, Content: text})
}
// httpPost sends a JSON POST request to the reasonix serve endpoint.
func (s *reasonixSession) httpPost(path string, body any) error {
var reqBody io.Reader
if body != nil {
data, err := json.Marshal(body)
if err != nil {
return fmt.Errorf("reasonix: marshal body: %w", err)
}
reqBody = bytes.NewReader(data)
}
req, err := http.NewRequestWithContext(s.ctx, "POST", s.serveURL+path, reqBody)
if err != nil {
return fmt.Errorf("reasonix: create request: %w", err)
}
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return fmt.Errorf("reasonix: POST %s: %w", path, err)
}
defer func() {
if err := resp.Body.Close(); err != nil {
slog.Warn("reasonix: POST close body", "path", path, "error", err)
}View on GitHub (pinned to 4000b2338a)
Solutions
- Inspect the wrapped error's json.MarshalTypeError field to find the offending Go type and field path
- Ensure the body passed to httpPost is a plain struct or map[string]any of JSON-safe values
- If options come from user config, sanitize/whitelist keys before building the request body
Example fix
// before: passing a body with a non-serializable field
body := map[string]any{"opts": s.opts} // opts contains a func
// after: build an explicit JSON-safe struct
body := map[string]any{"prompt": prompt, "session_id": s.id} Defensive patterns
Strategy: validation
Validate before calling
// pre-flight: ensure the body serializes
if b, err := json.Marshal(body); err != nil {
return fmt.Errorf("reasonix: invalid request body: %w", err)
} else { _ = b } Try / catch
// Go
if err := sess.Send(prompt, id, imgs, files); err != nil {
var me *json.MarshalTypeError
if errors.As(err, &me) {
slog.Error("non-serializable body field", "field", me.Field, "type", me.Type)
}
} Prevention
- Pass only plain structs or map[string]any of JSON-safe values as request bodies
- Never wrap funcs/channels into options maps
- Add a unit test marshaling every agent request-body type
When it happens
Trigger: Calling newSession, Send, or RespondPermission where the constructed request body (any) contains a value json.Marshal cannot encode — e.g. a channel, function value, cyclic pointer graph, or an invalid type nested in the options map.
Common situations: A custom config option in map[string]any passed through from config.toml holds a non-serializable value; a plugin wraps the session and injects an exotic body type; a Go struct with unexported-only fields producing empty JSON is a related (non-error) smell.
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
- marshal: %w
- codex app-server encode: %w
- marshal: %w
- piSession: marshal command: %w
- piSession: marshal extension_ui_response: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/adfa5c1d41520c93.
Report an issue: GitHub.