larksuite/cli · error
protocol encode: %w
Error message
protocol encode: %w
What it means
protocol.Encode wraps json.Marshal failures when serializing a message to the newline-delimited JSON wire protocol. Marshal only fails for unsupported types (channels, funcs, cyclic structures), so in practice this indicates a programmer error in the message struct, not bad wire data. The write error itself is returned unwrapped from w.Write.
Source
Thrown at internal/event/adapter/localbus/protocol/codec.go:48
// MaxFrameBytes bounds reader buffer growth. It must stay above
// MaxEventPayloadBytes, or the bus can frame an event the consumer then refuses
// to read.
const MaxFrameBytes = MaxEventPayloadBytes + maxFrameOverheadBytes
// ErrFrameTooLarge is returned by ReadFrame when a single frame exceeds MaxFrameBytes.
var ErrFrameTooLarge = errors.New("protocol: frame exceeds MaxFrameBytes")
const WriteTimeout = 5 * time.Second // bound writes against wedged peer kernel buffer
type typeEnvelope struct {
Type string `json:"type"`
}
func Encode(w io.Writer, msg interface{}) error {
data, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("protocol encode: %w", err)
}
data = append(data, '\n')
_, err = w.Write(data)
return err
}
func EncodeWithDeadline(conn net.Conn, msg interface{}, timeout time.Duration) error {
if err := conn.SetWriteDeadline(time.Now().Add(timeout)); err != nil {
return err
}
return Encode(conn, msg)
}
// ReadFrame reads one newline-delimited message; caps at MaxFrameBytes to defang slowloris.
func ReadFrame(br *bufio.Reader) ([]byte, error) {
var buf []byte
for {
chunk, err := br.ReadSlice('\n')View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Inspect the %w cause: it names the unsupported type or cycle
- Fix the message struct: remove or replace chan/func/complex fields with serializable types or json:"-" tags
- Break cyclic references before encoding (store IDs instead of back-pointers)
- Test with a unit encode of the concrete message type (see TestEncodeDecode*) before shipping
Example fix
// before
type Event struct {
Ack func() `json:"ack"`
}
// after
type Event struct {
Ack func() `json:"-"`
} Defensive patterns
Strategy: try-catch
Validate before calling
if err := json.Marshal(msg); err != nil {
return fmt.Errorf("message not encodable: %w", err)
}
// run before handing msg to protocol.Encode Type guard
func encodable(msg any) bool {
return json.Marshal(msg) == nil
} Try / catch
if err := protocol.Encode(w, msg); err != nil {
var ue *json.UnsupportedTypeError
var ce *json.UnsupportedValueError
switch {
case errors.As(err, &ue):
log.Printf("unsupported field type %s in %T", ue.Type, msg)
case errors.As(err, &ce):
log.Printf("unsupported value in %T: %v", msg, ce.Value)
default:
// write failure on w: check pipe/socket state
}
} Prevention
- Keep protocol message structs limited to JSON-serializable types (no chan/func fields without json:"-")
- Add round-trip tests (Encode+Decode) for every new message type
- Avoid cyclic pointers between Event and parent structures
- Validate messages in CI with a fuzz or example-based encode test
When it happens
Trigger: Calling Encode (directly or via handle's message flow) with a msg value that json.Marshal cannot serialize: an unmarshalable field type (chan, func, complex), a cyclic pointer graph, or an invalid utf-8/systr trick that makes Marshal error.
Common situations: Someone added a field of unsupported type to Hello/Event/StatusResponse structs; passing a raw map containing function values; embedding a context or sync.Mutex-like value that trips marshaling rules.
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
- protocol decode type: %w
- ErrMalformedConfig
- protocol: unknown message type %q
- marshal key %q: %w
- marshal value for %q: %w
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/a69e0db99bb22b02.
Report an issue: GitHub.