juicedata/juicefs · error

couldn't initialise SFTP

Error message

couldn't initialise SFTP

What it means

Raised by sftpConnection in pkg/object/sftp.go:95. After a successful SSH handshake, the client attempts sftp.NewClient(sshClient), which requests the "sftp" subsystem over the SSH connection. If the server does not run an SFTP subsystem (or the handshake with that subsystem fails), the SSH client is closed and this wrapped error is returned. It indicates the SSH endpoint is not SFTP-capable, not a network failure per se.

Source

Thrown at pkg/object/sftp.go:95

// Open a new connection to the SFTP server.
func (f *sftpStore) sftpConnection() (c *conn, err error) {
	c = &conn{
		err: make(chan error, 1),
	}
	conn, err := net.Dial("tcp", net.JoinHostPort(f.host, f.port))
	if err != nil {
		return nil, err
	}
	sshc, chans, reqs, err := ssh.NewClientConn(conn, net.JoinHostPort(f.host, f.port), f.config)
	if err != nil {
		return nil, err
	}
	c.sshClient = ssh.NewClient(sshc, chans, reqs)
	c.sftpClient, err = sftp.NewClient(c.sshClient)
	if err != nil {
		_ = c.sshClient.Close()
		return nil, errors.Wrap(err, "couldn't initialise SFTP")
	}
	go c.wait()
	return c, nil
}

// Get an SFTP connection from the pool, or open a new one
func (f *sftpStore) getSftpConnection() (c *conn, err error) {
	f.poolMu.Lock()
	for len(f.pool) > 0 {
		c = f.pool[0]
		f.pool = f.pool[1:]
		err := c.closed()
		if err == nil {
			break
		}
		c = nil
	}
	f.poolMu.Unlock()

View on GitHub (pinned to c9a67b23e8)

Solutions

  1. Enable the SFTP subsystem on the server: add 'Subsystem sftp internal-sftp' (or the sftp-server binary path) to sshd_config and reload sshd.
  2. Verify manually with: printf 'version\n' | sftp user@host (or ssh -s sftp user@host) to confirm the subsystem is offered.
  3. Confirm the sftp-server binary exists on the server (e.g. /usr/lib/openssh/sftp-server) if using an explicit Subsystem path.
  4. Check the user account is not restricted to a shell (nologin/rssh) that blocks subsystem requests.
  5. Inspect the wrapped underlying error for the precise server rejection reason.

Example fix

// server side: /etc/ssh/sshd_config
// before
#Subsystem sftp /usr/lib/openssh/sftp-server

// after
Subsystem sftp internal-sftp
// then: systemctl reload sshd
Defensive patterns

Strategy: validation

Validate before calling

// Check SFTP subsystem availability before configuring the store
conn, err := ssh.Dial("tcp", host, sshCfg)
if err != nil { return err }
sess, err := conn.NewSession()
if err != nil { return err }
err = sess.RequestSubsystem("sftp")
sess.Close()
conn.Close()
if err != nil {
    return fmt.Errorf("host %s does not provide the sftp subsystem: %w", host, err)
}

Try / catch

c, err := getSftpConnection(ctx)
if err != nil {
    if strings.Contains(err.Error(), "couldn't initialise SFTP") {
        // server lacks sftp subsystem: surface a clear config hint to the operator
    }
    return err
}

Prevention

When it happens

Trigger: Creating an SFTP object store where the remote sshd has no "Subsystem sftp" configured, the sftp-server binary is missing, the user's shell/subsystem is restricted (e.g. forced internal-sftp with wrong path, or rssh/nologin shell), or the server rejects the subsystem request.

Common situations: Minimal container/SSH servers without openssh-sftp-server installed; hardened sshd_config with Subsystem removed; restricted SFTP chroot setups that reject the client; connecting to a non-OpenSSH server lacking SFTP support.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of juicedata/juicefs@c9a67b23e8 (2026-09-06). Data as JSON: /api/errors/7006e289ad802fb8. Report an issue: GitHub.