chenhg5/cc-connect · warning

piSession: marshal command: %w

Error message

piSession: marshal command: %w

What it means

agent/pi/session.go:454 — writeRPCCommand marshals the command map to JSON before writing it to the RPC stdin; if json.Marshal fails it returns "piSession: marshal command: %w". The commands built by this library (get_state probe, prompt commands) are plain map[string]any of JSON-safe values, so this nearly always indicates non-serializable data (channels, funcs, or invalid values like NaN) injected into the command payload — e.g. via prompt or attachment path content.

Source

Thrown at agent/pi/session.go:454

	}
	sid := s.CurrentSessionID()
	evt := core.Event{Type: core.EventResult, SessionID: sid, Done: true}
	select {
	case s.events <- evt:
	case <-s.ctx.Done():
	}

	return nil
}

// writeRPCCommand marshals cmd as a single JSONL line and writes it to the
// RPC process's stdin under rpcStdinMu. Used by both sendRPC (for "prompt"
// commands during a turn) and startRPC (for the startup "get_state" probe
// that fetches the session id before callers are released).
func (s *piSession) writeRPCCommand(cmd map[string]any) error {
	b, err := json.Marshal(cmd)
	if err != nil {
		return fmt.Errorf("piSession: marshal command: %w", err)
	}
	b = append(b, '\n')

	s.rpcStdinMu.Lock()
	_, err = s.rpcStdin.Write(b)
	s.rpcStdinMu.Unlock()
	if err != nil {
		return fmt.Errorf("piSession: write stdin: %w", err)
	}
	return nil
}

// sendRPC writes a JSON "prompt" command to the persistent RPC process stdin.
// Events are read asynchronously by readLoopRPC, including agent_end which
// triggers EventResult.
//
// Issue #1723: image paths are embedded into the message text as
// @<path> references (pi's standard mechanism, parsed the same way as in

View on GitHub (pinned to 4000b2338a)

Solutions

  1. Inspect the wrapped json error (UnsupportedTypeError names the offending Go type) and remove/convert that value.
  2. Sanitize payload values: ensure prompt text, image paths, and file paths are plain strings.
  3. Replace NaN/Inf floats with strings or omit them from the command map.
  4. If you maintain custom pi-agent code, validate the command with json.Marshal in a test before calling writeRPCCommand.

Example fix

// before
cmd := map[string]any{"type": "prompt", "message": msg, "cb": callback} // func not marshalable
// after
cmd := map[string]any{"type": "prompt", "message": msg}
Defensive patterns

Strategy: validation

Validate before calling

func validateRPCCommand(cmd map[string]any) error {
    if _, err := json.Marshal(cmd); err != nil {
        return fmt.Errorf("command not JSON-serializable: %w", err)
    }
    return nil
}

Try / catch

if err := validateRPCCommand(cmdMap); err != nil {
    return fmt.Errorf("refusing to send invalid RPC command: %w", err)
}
if err := writeRPCCommand(cmdMap); err != nil {
    if strings.Contains(err.Error(), "marshal command:") {
        slog.Error("RPC command marshal failed; sanitize payload", "err", err)
    }
    return err
}

Prevention

When it happens

Trigger: writeRPCCommand is called by startRPC (get_state probe) and sendRPC (prompt commands); marshal fails when: (1) a value in the command map is not JSON-marshalable (chan, func, complex); (2) an unsupported value type slips in via extra args/attachment lists; (3) json.Marshal returns json.UnsupportedTypeError / UnsupportedValueError for float NaN/Inf.

Common situations: Custom code or a fork injecting non-JSON-safe fields into the prompt command; NaN/Inf values propagated into the payload; misuse of the internal API by plugin/extension code building commands with richer 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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/e93e43c4ec9d8deb. Report an issue: GitHub.