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.
Solutions
- Ensure the session is fully started and authenticated (await session.start()) before calling addPortForward for a Remote forward.
- Gate the call on the instance check: only add the remote forward once this.ssh instanceof russh.AuthenticatedSSHClient.
- Configure remote forwards via profile.options.forwardedPorts so they are applied after auth success at ssh.ts:493, not before.
- 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
- Await session.start() before adding remote forwards programmatically.
- Prefer declaring remote forwards in profile.options.forwardedPorts so they apply post-auth.
- Never call addPortForward from a pre-auth or disconnect handler.
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
- Cannot open shell channel before auth
- Cannot remove remote port forward before auth
- Authentication rejected
- Download cancelled
- File handle is closed
AI-assisted analysis of Eugeny/tabby@14e2d60b9b (2026-08-12).
Data as JSON: /api/errors/a927981ac683a2ad.
Report an issue: GitHub.
Appendix: 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)