{"record":{"id":"ff941141706c1786","repo":"Eugeny/tabby","slug":"authentication-rejected","errorCode":null,"errorMessage":"Authentication rejected","messagePattern":"Authentication rejected","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"tabby-ssh/src/session/ssh.ts","lineNumber":484,"sourceCode":"        if (this.authUsername?.startsWith('$')) {\n            try {\n                const result = process.env[this.authUsername.slice(1)]\n                this.authUsername = result ?? this.authUsername\n            } catch {\n                this.authUsername = 'root'\n            }\n        }\n\n        await this.populateStoredPasswordsForResolvedUsername()\n\n        const authenticatedClient = await this.handleAuth()\n        if (authenticatedClient) {\n            this.ssh = authenticatedClient\n        } else {\n            this.ssh.disconnect()\n            this.passwordStorage.deletePassword(this.profile, this.authUsername ?? undefined)\n            // eslint-disable-next-line @typescript-eslint/no-base-to-string\n            throw new Error('Authentication rejected')\n        }\n\n        // auth success\n\n        if (this.savedPassword) {\n            this.passwordStorage.savePassword(this.profile, this.savedPassword, this.authUsername ?? undefined)\n        }\n\n        for (const fw of this.profile.options.forwardedPorts) {\n            this.addPortForward(Object.assign(new ForwardedPort(), fw))\n        }\n\n        this.open = true\n\n        this.ssh.tcpChannelOpen$.subscribe(async event => {\n            this.logger.info(`Incoming forwarded connection: ${event.clientAddress}:${event.clientPort} -> ${event.targetAddress}:${event.targetPort}`)\n\n            if (!(this.ssh instanceof russh.AuthenticatedSSHClient)) {","sourceCodeStart":466,"sourceCodeEnd":502,"githubUrl":"https://github.com/Eugeny/tabby/blob/14e2d60b9b6dee84a53c37f05eefeb803787de04/tabby-ssh/src/session/ssh.ts#L466-L502","documentation":"Thrown by the SSH session after handleAuth() exhausts every configured authentication method (or detects a prior disconnect) and returns null. It is the terminal signal that the server rejected all offered credentials. Before throwing, the session disconnects the transport and deletes any stored password for the profile, so the stored credential is cleared on hard failure.","triggerScenarios":"Reached when this.handleAuth() (ssh.ts ~477) resolves to null. _handleAuth returns null when this.previouslyDisconnected is true, or when no remaining AuthMethod matches the server's allowed methods (methodsLeft). Concretely: every saved-password, prompt-password, publickey, keyboard-interactive, and agent attempt either returned an AuthFailure or threw, and no method is left to try.","commonSituations":"Wrong password / changed server password; private key no longer authorized in ~/.ssh/authorized_keys; server disabled 'password' auth and only allows 'publickey' but the user configured password-only; SSH agent not running or identity not loaded; account locked or AllowUsers/DenyUsers excludes the user; keyboard-interactive challenges answered incorrectly until the server stops offering methods.","solutions":["Verify credentials work with the OS openssh client: `ssh -v <user>@<host>` and compare the allowed auth methods it lists.","In the profile, enable an auth method the server actually offers (check 'Auth methods' returned by the failed handshake); add a valid private key, or start ssh-agent and add the identity.","Confirm the username is correct and not being mangled by the $ENV or prompt fallback (see authUsername resolution near ssh.ts:460).","If using keyboard-interactive/2FA, ensure the prompts are answered and the server still offers the method after failures.","Re-enter the password so a fresh one is saved, since the bad stored password was just deleted."],"exampleFix":"// before: profile only has 'prompt-password' but server requires publickey\n// after: add a key-based method in the profile auth options, or load it into the agent\nssh-add ~/.ssh/id_ed25519\n// then in Tabby profile -> Credentials -> pick 'Private key' and select the key","handlingStrategy":"try-catch","validationCode":"// Auth outcome cannot be pre-validated; verify config shape before starting.\nconst methods = profile.options.authMethods ?? []\nif (methods.length === 0 && !profile.options.password && !profile.options.privateKey) {\n    throw new Error('No auth method configured; auth will be rejected')\n}","typeGuard":null,"tryCatchPattern":"try {\n    await session.start()\n} catch (e) {\n    if (e.message === 'Authentication rejected') {\n        // prompt user to fix credentials / add a key method, then retry start()\n    } else { throw e }\n}","preventionTips":["Always configure at least one key-based auth method in addition to password.","Test credentials with the OS ssh client before configuring the profile.","Keep the SSH agent running with the identity loaded for publickey/agent auth."],"tags":["ssh","authentication","russh","credentials"],"backgroundTag":null,"analyzedSha":"14e2d60b9b6dee84a53c37f05eefeb803787de04","analyzedAt":"2026-08-12T11:46:48.773Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}