This error occurs when the SSH server fails to allocate a terminal (PTY) or shell session for your connection. Common causes include semaphore exhaustion, misconfigured /dev/pts mount options, SSH settings restrictions, or system resource limits.
The 'shell request failed on channel 0' error means the SSH daemon successfully authenticated your connection but then failed to start an interactive shell session. Channel 0 refers to the first communication channel in the SSH protocol. This typically happens during the pseudo-terminal (PTY) allocation phase when the server tries to set up a terminal environment for your session. Unlike a simple authentication failure, this error indicates the connection succeeded but couldn't proceed to create a usable shell.
First, confirm whether the SSH daemon is working at all by running a single command instead of requesting an interactive shell.
# This should work even if shell request fails
ssh user@hostname echo "SSH works"
# This will fail if shell request fails
ssh user@hostnameIf the echo command succeeds, the issue is specifically with PTY/shell allocation rather than general SSH connectivity. This narrows down the problem significantly.
The most common cause is semaphore exhaustion. Check how many semaphores are in use on the system.
# View all semaphore sets
ipcs -s
# View summary
ipcs -s -s
# Count total semaphores in use
ipcs -s | tail -n +4 | awk '{sum += $3} END {print "Total semaphores:", sum}'If the count is very high (approaching system limit), you need to free them.
# View semaphore limits
cat /proc/sys/kernel/sem
# Output format: SEMMSL SEMMNS SEMOPM SEMMNI
# SEMMNS = max semaphores system-wide (default 32000)If you're at or near the limit, free semaphores from idle processes:
# Remove a specific semaphore set by ID (from ipcs output)
ipcrm -s <semid>
# Or free all semaphores from a specific user
ipcrm -s $(ipcs -s | grep username | awk '{print $2}')Check that the /dev/pts filesystem is properly mounted with correct options.
# Check if /dev/pts is mounted
mount | grep pts
# You should see something like:
# devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620)If /dev/pts is missing or has incorrect options, remount it:
# Unmount and remount /dev/pts
ubuntu@host:~$ sudo umount /dev/pts
ubuntu@host:~$ sudo mount -t devpts -o "gid=5,mode=620" none /dev/ptsFor permanent fix, ensure /etc/fstab has the correct entry:
# Add or verify this line in /etc/fstab
echo "none /dev/pts devpts defaults 0 0" | sudo tee -a /etc/fstab
# If the line already exists with different options, edit it
sudo nano /etc/fstabThen reboot or remount to apply the change.
Verify sshd_config doesn't have PTY restrictions that would prevent shell allocation.
# View the relevant SSH config lines
sudo grep -i "PermitTTY\|PermitOpen\|Subsystem" /etc/ssh/sshd_configLook for these problematic entries:
PermitTTY no # This prevents any TTY allocation
PermitOpen localhost # This restricts tunneling but shouldn't affect shellsIf PermitTTY is set to no, change it to yes:
# Edit the config
sudo nano /etc/ssh/sshd_config
# Change line to:
PermitTTY yes
# Validate syntax
sudo sshd -t
# Restart SSH daemon
sudo systemctl restart sshdAlso check for duplicate entries (especially Subsystem lines) which prevent the daemon from starting.
Ensure the user's login shell is valid and exists on the system.
# Check what shell is assigned to the user
grep username /etc/passwd
# Output format: username:x:uid:gid:comment:/home/username:/bin/bash
# Verify the shell exists and is executable
ls -la /bin/bash
# List available shells
cat /etc/shellsIf the shell doesn't exist, update the user's shell:
# Change shell to /bin/bash (or another shell from /etc/shells)
sudo usermod -s /bin/bash username
# Or edit /etc/passwd directly
sudo nano /etc/passwdCommon valid shells: /bin/bash, /bin/sh, /bin/zsh, /bin/ksh. Never set it to /bin/false or /usr/sbin/nologin for interactive users.
If you're using public key authentication, verify the authorized_keys file doesn't have restrictive options that block TTY.
# View your authorized_keys file
cat ~/.ssh/authorized_keys
# Look for problematic prefixes like:
# restrict,command="/usr/bin/no-pty-command" ssh-rsa AAAA...
# The 'restrict' or 'command=' options can prevent shell allocationIf you see restrictions, either remove them or modify them to allow TTY:
# Backup first
cp ~/.ssh/authorized_keys ~/.ssh/authorized_keys.bak
# Remove the restrict/command prefix (keep the ssh-rsa AAAA... key part)
# Before: restrict,command="/path" ssh-rsa AAAA...
# After: ssh-rsa AAAA...
sudo nano ~/.ssh/authorized_keysIf you need command restrictions but also need TTY, use less restrictive options.
Check how many pseudo-terminal devices are available and in use.
# List all PTY devices
ls -la /dev/pts/
# Count them
ls -la /dev/pts/ | tail -n +4 | wc -l
# View max PTYs allowed
cat /proc/sys/kernel/pty/max
# Default is 4096If many PTYs are in use, processes might be leaking them:
# Find processes using PTYs
lsof | grep pts
# Kill idle processes consuming PTYs
ps aux | grep defunct # Show zombie processes
killall -9 <process_name> # Kill stuck processesIf you consistently hit PTY limits, increase the max (requires root):
# Temporarily increase PTY limit
echo 8096 | sudo tee /proc/sys/kernel/pty/max
# For permanent change, add to /etc/sysctl.conf
echo "kernel.pty.max = 8096" | sudo tee -a /etc/sysctl.conf
sudo sysctl -pIf semaphore exhaustion is the problem, a reboot will immediately free all semaphores and reset PTY allocations.
# Graceful reboot
sudo reboot
# Or more forcefully
sudo shutdown -r nowAfter reboot, test SSH login immediately:
ssh user@hostname
# Should now connect to shell without errorIf this fixes the issue but it recurs after a few days, investigate which process is leaking semaphores. Check for:
- Long-running SSH tunnels or multiplexed connections
- Processes in defunct/zombie state
- Custom applications spawning many SSH connections
- Cron jobs or scripts that don't clean up SSH sessions properly
On Windows systems with OpenSSH for Windows, the error may occur if the system doesn't know which shell to use.
# Check OpenSSH version
OpenSSH.exe -V
# Check the default shell registry setting
Reg query HKLM\SOFTWARE\OpenSSH /v DefaultShell
# If not set, set PowerShell as default
Reg add HKLM\SOFTWARE\OpenSSH /v DefaultShell /t REG_SZ /d C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe /f
# Or set cmd.exe
Reg add HKLM\SOFTWARE\OpenSSH /v DefaultShell /t REG_SZ /d C:\Windows\System32\cmd.exe /fThen restart the SSH service:
Restart-Service -Name sshd -ForceAfter the change, test SSH login from another machine.
If the above steps don't resolve the issue, enable debug logging on the server to see exactly where shell allocation fails.
# Edit sshd_config to enable debug logging
sudo nano /etc/ssh/sshd_config
# Add this line:
SyslogFacility AUTH
LogLevel DEBUG
# Or use even more verbose:
LogLevel DEBUG2
LogLevel DEBUG3
# Save and validate
sudo sshd -t
# Restart sshd
sudo systemctl restart sshd
# Now attempt a connection from a client and watch the logs
sudo tail -f /var/log/auth.log
# or on macOS
sudo log stream --predicate 'process == "sshd"' --level debugThe debug output will show exactly which step fails (semaphore allocation, PTY creation, etc.) and provide error codes to guide further troubleshooting.
Once you've resolved the issue, set LogLevel back to INFO or VERBOSE to reduce log verbosity.
On systems with SELinux enabled, incorrect SELinux contexts on /dev/pts or SSH binaries can prevent PTY allocation. Run restorecon -R /dev/pts to restore default contexts.
For cloud instances (AWS EC2, Azure, DigitalOcean), the 'shell request failed' error may indicate the instance is running out of memory and has killed background processes. Check dmesg | tail -20 for OOM (out of memory) killer messages and monitor swap usage with free -h.
If running SSH inside containers (Docker, LXC), ensure the container has /dev/pts mounted correctly and isn't sharing PTY resources with too many other processes. Use docker inspect to verify mount options.
For Ansible and Terraform users, this error breaks all remote execution. The workaround is running non-PTY commands: in Ansible use pty: false in tasks, and in Terraform use command provisioners instead of shell provisioners when possible.
Some Linux distributions (especially Arch) use newer udev rules that mount /dev/pts differently. If remounting doesn't work, check /etc/udev/rules.d/ for any custom devpts rules that override the default.
On MacOS, if this error occurs, ensure the /dev filesystem has proper permissions. Run ls -la /dev | grep pts and verify ownership is root with reasonable permissions.
Someone could be eavesdropping on you right now (man-in-the-middle attack)!
How to fix SSH man-in-the-middle attack warning in SSH
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: UNPROTECTED PRIVATE KEY FILE! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
How to fix "WARNING: UNPROTECTED PRIVATE KEY FILE!" in SSH
sign_and_send_pubkey: no mutual signature supported
How to fix "sign_and_send_pubkey: no mutual signature supported" in SSH
Bad owner or permissions on /home/user/.ssh/known_hosts
How to fix "Bad owner or permissions on known_hosts" in SSH
It is required that your private key files are NOT accessible by others.
How to fix "private key files are NOT accessible by others" in SSH