ginuerzh/gost · error · net.OpError

write not supported

Error message

write not supported

What it means

The Write method of sshNopConn is a deliberate stub: it always returns a *net.OpError (Op "write", Net "ssh") wrapping "write not supported". This variant of the nop conn exists where the connection is only meant to be read from; writing to it is a usage error.

Source

Thrown at ssh.go:910

				Extensions: map[string]string{
					"pubkey-fp": ssh.FingerprintSHA256(pubKey),
				},
			}, nil
		}
		return nil, fmt.Errorf("unknown public key for %q", c.User())
	}
}

type sshNopConn struct {
	session *sshSession
}

func (c *sshNopConn) Read(b []byte) (n int, err error) {
	return 0, &net.OpError{Op: "read", Net: "ssh", Source: nil, Addr: nil, Err: errors.New("read not supported")}
}

func (c *sshNopConn) Write(b []byte) (n int, err error) {
	return 0, &net.OpError{Op: "write", Net: "ssh", Source: nil, Addr: nil, Err: errors.New("write not supported")}
}

func (c *sshNopConn) Close() error {
	return nil
}

func (c *sshNopConn) LocalAddr() net.Addr {
	return &net.TCPAddr{
		IP:   net.IPv4zero,
		Port: 0,
	}
}

func (c *sshNopConn) RemoteAddr() net.Addr {
	return &net.TCPAddr{
		IP:   net.IPv4zero,
		Port: 0,
	}

View on GitHub (pinned to a33fdbf4c9)

Solutions

  1. Route writes through the real ssh channel-backed connection (sshConn) instead of the nop conn
  2. Audit the caller: if only reads are expected, remove the write path or assert the conn type before writing
  3. Handle *net.OpError with Op "write" and Net "ssh" distinctly to surface this misuse early

Example fix

// before
conn.Write(req) // conn is sshNopConn -> write not supported
// after
channel.Write(req) // write via the ssh channel
Defensive patterns

Strategy: type-guard

Validate before calling

func canWrite(c net.Conn) bool {
    _, nop := c.(*sshNopConn)
    return !nop
}

Type guard

func asWriter(c net.Conn) (io.Writer, bool) {
    if _, nop := c.(*sshNopConn); nop { return nil, false }
    return c, true
}

Try / catch

_, err := conn.Write(data)
if err != nil {
    var opErr *net.OpError
    if errors.As(err, &opErr) && opErr.Net == "ssh" && opErr.Op == "write" {
        return fmt.Errorf("ssh conn is read-only: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Write on a *sshNopConn, e.g. io.Copy(nopConn, src), conn.Write(payload), or an HTTP/TLS layer attempting to send bytes over this conn.

Common situations: Using the nop conn as a generic net.Conn in a transport that sends a request; misconfiguration where the write-side channel was supposed to be the real ssh channel.

Related errors


AI-assisted analysis of ginuerzh/gost@a33fdbf4c9 (2026-09-02). Data as JSON: /api/errors/850a81a934371f65. Report an issue: GitHub.