henrygd/beszel · error
timeout
Error message
timeout
What it means
createSessionWithTimeout bounds SSH session creation with context.WithTimeout. If the goroutine dialing/opening the session does not produce a session or error before the deadline, ctx.Done() fires and the call returns the literal error 'timeout'. The underlying goroutine is abandoned (the session, if it later succeeds, leaks).
Solutions
- Verify network reachability/latency to the agent host (ping, ssh -v from the hub host)
- Increase the SSH session timeout configured in the hub settings
- Check the agent host's sshd is running and not throttling (MaxStartups) and that the firewall allows port 22
- Reduce concurrent SSH session creation storms; rely on the manager's reconnect logic
Example fix
// before session, err := sys.createSessionWithTimeout(5 * time.Second) // after session, err := sys.createSessionWithTimeout(30 * time.Second) // longer timeout for slow links
Defensive patterns
Strategy: retry
Validate before calling
conn, err := net.DialTimeout("tcp", host+":22", 5*time.Second)
if err != nil {
return fmt.Errorf("agent host unreachable before SSH attempt: %w", err)
} Try / catch
session, err := sys.createSessionWithTimeout(timeout)
if err != nil && err.Error() == "timeout" {
// backoff then retry once
time.Sleep(2 * time.Second)
session, err = sys.createSessionWithTimeout(timeout * 2)
}
return err Prevention
- Configure a timeout generous enough for WAN links to agent hosts
- Monitor hub-agent latency and alert on degradation
- Ensure sshd on agents is reachable and not throttling (MaxStartups)
- Avoid firewalls that silently drop packets causing hang-until-timeout
When it happens
Trigger: The ssh.Dial / client.NewSession call in the spawned goroutine exceeds the configured timeout — slow network to the agent host, SSH server not responding, DNS stalls, or an unreachable host where TCP connect hangs past the deadline.
Common situations: Firewall dropping packets silently (no RST) so the TCP handshake hangs; heavily loaded agent host; SSH server MaxStartups throttling new connections; transient network partition between hub and agent.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- timeout creating session
- client not initialized
- ( ) failed to fetch latest releases
- ( ) failed to send download file request
- failed to decode response
AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31).
Data as JSON: /api/errors/42581e0bda9feddc.
Report an issue: GitHub.
Appendix: source
Thrown at internal/hub/systems/system.go:790
sessionChan := make(chan *ssh.Session, 1)
errChan := make(chan error, 1)
go func() {
if session, err := client.NewSession(); err != nil {
errChan <- err
} else {
sessionChan <- session
}
}()
select {
case session := <-sessionChan:
return session, nil
case err := <-errChan:
return nil, err
case <-ctx.Done():
return nil, fmt.Errorf("timeout")
}
}
// closeSSHConnection closes the SSH connection but keeps the system in the manager
func (sys *System) closeSSHConnection() {
if sys.sshTransport != nil {
sys.sshTransport.Close()
}
if client := sys.client.Swap(nil); client != nil {
client.Close()
}
}
// closeWebSocketConnection closes the WebSocket connection but keeps the system in the manager
// to allow updating via SSH. It will be removed if the WS connection is re-established.
// The system will be set as down a few seconds later if the connection is not re-established.
func (sys *System) closeWebSocketConnection() {
if sys.WsConn != nil {View on GitHub (pinned to b38fb7dafa)