yudai/gotty · error

received malformed remote command for terminal resize: empty

Error message

received malformed remote command for terminal resize: empty payload

What it means

When a ResizeTerminal message arrives before columns/rows are known, WebTTY requires a JSON payload after the 1-byte message-type prefix. If len(data) <= 1 there is no payload to unmarshal, so this descriptive error is returned instead of a confusing json error.

Source

Thrown at webtty/webtty.go:190

		_, err := wt.slave.Write(data[1:])
		if err != nil {
			return errors.Wrapf(err, "failed to write received data to slave")
		}

	case Ping:
		err := wt.masterWrite([]byte{Pong})
		if err != nil {
			return errors.Wrapf(err, "failed to return Pong message to master")
		}

	case ResizeTerminal:
		if wt.columns != 0 && wt.rows != 0 {
			break
		}

		if len(data) <= 1 {
			return errors.New("received malformed remote command for terminal resize: empty payload")
		}

		var args argResizeTerminal
		err := json.Unmarshal(data[1:], &args)
		if err != nil {
			return errors.Wrapf(err, "received malformed data for terminal resize")
		}
		rows := wt.rows
		if rows == 0 {
			rows = int(args.Rows)
		}

		columns := wt.columns
		if columns == 0 {
			columns = int(args.Columns)
		}

		wt.slave.ResizeTerminal(columns, rows)

View on GitHub (pinned to a080c85cbc)

Solutions

  1. Send the full frame: type byte followed by JSON like {"columns":80,"rows":24}
  2. Fix the client encoder so the payload is appended after the message-type byte
  3. Check intermediate proxies/websocket bridges for frame truncation

Example fix

// before
conn.WriteMessage(ws.TextMessage, []byte{ResizeTerminal})
// after
payload, _ := json.Marshal(argResizeTerminal{Columns: 80, Rows: 24})
msg := append([]byte{ResizeTerminal}, payload...)
conn.WriteMessage(ws.TextMessage, msg)
Defensive patterns

Strategy: validation

Validate before calling

func validResizeFrame(msg []byte) bool {
    return len(msg) > 1 && json.Valid(msg[1:])
}

Type guard

func isResizeFrame(msg []byte, resize byte) bool {
    return len(msg) > 0 && msg[0] == resize && len(msg) > 1
}

Try / catch

if err := conn.WriteControl/writeMsg(frame); err != nil || !validResizeFrame(frame) {
    // fix encoder before sending
}

Prevention

When it happens

Trigger: Client sends a single-byte message with the ResizeTerminal type identifier and no JSON arguments following it, while wt.columns==0 && wt.rows==0 (initial dims unset).

Common situations: Custom websocket clients that send only the command byte; truncated frames from a buggy proxy; hand-written clients that forget the JSON body {columns, rows}.

Understand the failure class


AI-assisted analysis of yudai/gotty@a080c85cbc (2026-09-02). Data as JSON: /api/errors/3d8c3a7b814bb93b. Report an issue: GitHub.