Eugeny/tabby · error · Error

Cannot add remote port forward before auth

Error message

Cannot add remote port forward before auth

What it means

Thrown by addPortForward() when forwarding type is Remote (server-side -R forward) but this.ssh is not yet an AuthenticatedSSHClient. Remote forwards require the authenticated transport to call forwardTCPPort on the server, so an unauthenticated session cannot set one up.

Source

Thrown at tabby-ssh/src/session/ssh.ts:818

                }).catch(err => {
                    this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote has rejected the forwarded connection to ${targetAddress}:${targetPort} via ${fw}: ${err}`)
                    reject()
                    throw err
                }))
                const socket = accept()

                this.setupSocketChannelEvents(channel, socket, 'Local forward')
            }).then(() => {
                this.emitServiceMessage(colors.bgGreen.black(' -> ') + ` Forwarded ${fw}`)
                this.forwardedPorts.push(fw)
            }).catch(e => {
                this.emitServiceMessage(colors.bgRed.black(' X ') + ` Failed to forward port ${fw}: ${e}`)
                throw e
            })
        }
        if (fw.type === PortForwardType.Remote) {
            if (!(this.ssh instanceof russh.AuthenticatedSSHClient)) {
                throw new Error('Cannot add remote port forward before auth')
            }
            try {
                await this.ssh.forwardTCPPort(fw.host, fw.port)
            } catch (err) {
                // eslint-disable-next-line @typescript-eslint/no-base-to-string
                this.emitServiceMessage(colors.bgRed.black(' X ') + ` Remote rejected port forwarding for ${fw}: ${err}`)
                return
            }
            this.emitServiceMessage(colors.bgGreen.black(' <- ') + ` Forwarded ${fw}`)
            this.forwardedPorts.push(fw)
        }
    }

    async removePortForward (fw: ForwardedPort): Promise<void> {
        if (fw.type === PortForwardType.Local || fw.type === PortForwardType.Dynamic) {
            fw.stopLocalListener()
            this.forwardedPorts = this.forwardedPorts.filter(x => x !== fw)
        }

View on GitHub (pinned to 14e2d60b9b)

Solutions

  1. Ensure the session is fully started and authenticated (await session.start()) before calling addPortForward for a Remote forward.
  2. Gate the call on the instance check: only add the remote forward once this.ssh instanceof russh.AuthenticatedSSHClient.
  3. Configure remote forwards via profile.options.forwardedPorts so they are applied after auth success at ssh.ts:493, not before.
  4. If the session lost auth mid-flight, re-establish it before retrying the forward.

Example fix

// before
session.addPortForward(remoteFw) // may run before auth
// after
await session.start()
if (session.ssh instanceof russh.AuthenticatedSSHClient) {
    await session.addPortForward(remoteFw)
}
Defensive patterns

Strategy: type-guard

Validate before calling

import * as russh from 'russh'
if (fw.type === PortForwardType.Remote &&
    !(session.ssh instanceof russh.AuthenticatedSSHClient)) {
    throw new Error('Wait for SSH auth before adding a remote port forward')
}

Type guard

import * as russh from 'russh'
function isAuthed (s: unknown): s is russh.AuthenticatedSSHClient {
    return s instanceof russh.AuthenticatedSSHClient
}

Prevention

When it happens

Trigger: addPortForward(fw) called with fw.type === PortForwardType.Remote while this.ssh is still a plain russh.SSHClient (pre-auth) or has been disconnected. Can occur if a profile's options.forwardedPorts are applied before auth completes, or if a caller invokes addPortForward manually right after constructing the session.

Common situations: Programmatic use that adds a Remote forward before awaiting the session start/auth; a race where addPortForward runs during reconnection before re-auth finishes; profile with remote forwards loaded into a session whose auth subsequently failed but the forward list is still being applied.

Related errors


AI-assisted analysis of Eugeny/tabby@14e2d60b9b (2026-08-12). Data as JSON: /api/errors/a927981ac683a2ad. Report an issue: GitHub.