chenhg5/cc-connect · error
piSession: marshal extension_ui_response: %w
Error message
piSession: marshal extension_ui_response: %w
What it means
RespondPermission in agent/pi/session.go fails when it cannot JSON-marshal the extension_ui_response payload it is about to send back to pi. Because the response struct is built from simple fields (id, behavior), a marshal failure almost always indicates a programming error such as an unsupported value type (e.g. a channel or func accidentally placed in the options map).
Source
Thrown at agent/pi/session.go:1192
case "confirm":
// extension_confirm goes through the regular permission flow, not the
// AskUserQuestion flow, so the engine sends plain "allow"/"deny"
// PermissionResults (no UpdatedInput.answers). Forward as the default
// case: confirmed=Behavior=="allow", else confirmed=false (pgate's
// ctx.ui.confirm resolves false for both "deny" and "no confirmation",
// which is what we want).
fallthrough
default:
resp = map[string]any{
"type": "extension_ui_response",
"id": extID,
"confirmed": result.Behavior == "allow",
}
}
b, err := json.Marshal(resp)
if err != nil {
return fmt.Errorf("piSession: marshal extension_ui_response: %w", err)
}
b = append(b, '\n')
slog.Debug("piSession: sending extension_ui_response", "id", extID, "behavior", result.Behavior)
s.rpcStdinMu.Lock()
_, err = s.rpcStdin.Write(b)
s.rpcStdinMu.Unlock()
if err != nil {
return fmt.Errorf("piSession: write extension_ui_response: %w", err)
}
return nil
}
// ── AgentSession interface ──────────────────────────────────
func (s *piSession) Events() <-chan core.Event {
return s.eventsView on GitHub (pinned to 4000b2338a)
Solutions
- Inspect the wrapped json error to find the offending field/type
- Ensure every value in the response is a JSON-safe type (string, bool, number, map, slice)
- Add json tags to any custom struct fields
- Add a unit test covering RespondPermission payload marshalling
Example fix
// before
resp := map[string]any{"id": extID, "callback": result.Callback}
b, err := json.Marshal(resp)
// after
resp := map[string]any{"id": extID, "confirmed": result.Behavior == "allow"}
b, err := json.Marshal(resp) // only JSON-safe values Defensive patterns
Strategy: validation
Validate before calling
if _, err := json.Marshal(resp); err != nil {
slog.Error("permission response not serializable", "err", err)
} Type guard
func jsonSafe(v any) bool {
_, err := json.Marshal(v)
return err == nil
} Try / catch
if err := sess.RespondPermission(ctx, id, result); err != nil {
if strings.Contains(err.Error(), "marshal") {
slog.Error("bad permission payload", "err", err) // code bug, do not retry
}
return err
} Prevention
- Only put JSON-safe types in the response map
- Add json tags to custom structs
- Unit-test RespondPermission payload marshalling
- Treat marshal errors as programming bugs, not transient failures
When it happens
Trigger: Calling RespondPermission on a pi RPC session where the constructed resp map/struct contains a value json.Marshal cannot encode; effectively only reachable via code changes adding non-serializable fields.
Common situations: A developer extended the permission-response payload with a non-JSON-encodable type; custom struct without json tags holding unsupported types.
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
- marshal: %w
- piSession: marshal command: %w
- mimo tts: marshal request: %w
- marshal payload: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/9e08cee56d16ccc1.
Report an issue: GitHub.