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).

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)

Solutions

  1. Verify network reachability/latency to the agent host (ping, ssh -v from the hub host)
  2. Increase the SSH session timeout configured in the hub settings
  3. Check the agent host's sshd is running and not throttling (MaxStartups) and that the firewall allows port 22
  4. 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

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

Related errors


AI-assisted analysis of henrygd/beszel@b38fb7dafa (2026-08-31). Data as JSON: /api/errors/42581e0bda9feddc. Report an issue: GitHub.