wavetermdev/waveterm · error

error getting environment without PTY

Error message

error getting environment without PTY: %w

What it means

After obtaining the ssh client, GetEnvironmentMaps runs a first probe that executes a command WITHOUT allocating a PTY to capture the environment. Any failure in that session (command error, channel failure, dropped connection) is wrapped as 'error getting environment without PTY'.

Solutions

  1. Inspect the wrapped inner error for the actual SSH/shell failure
  2. Test manual non-interactive SSH command execution (ssh host 'env') to reproduce
  3. Fix remote shell startup files that error or emit unexpected output in non-interactive mode
  4. Check sshd MaxSessions and connection stability

Example fix

// before
noPtyEnv, err := conn.getEnvironmentNoPty(ctx, client)
// remote .bashrc prints to stdout, corrupting/failing the env probe
// after: guard profile output
[[ $- == *i* ]] || return   // in ~/.bashrc: skip setup for non-interactive shells
Defensive patterns

Strategy: try-catch

Validate before calling

// non-interactive probe precheck
out, err := client.CombinedOutput("true")
if err != nil {
    return fmt.Errorf("remote cannot run commands: %v", err)
}

Try / catch

noPty, pty, err := conn.GetEnvironmentMaps(ctx)
if err != nil && strings.Contains(err.Error(), "environment without PTY") {
    log.Printf("env probe (no pty) failed: %v", err) // inspect wrapped cause
}

Prevention

When it happens

Trigger: conn.getEnvironmentNoPty returns an error — the remote shell command fails, the SSH channel cannot be opened, or the connection drops during the probe.

Common situations: Remote shell startup errors (broken .bashrc/.profile emitting output or failing); sshd MaxSessions exhausted; connection reset mid-command; restricted shell that forbids command execution.

Related errors


AI-assisted analysis of wavetermdev/waveterm@a4447c1563 (2026-09-01). Data as JSON: /api/errors/b1658b04335ba467. Report an issue: GitHub.

Appendix: source

Thrown at pkg/remote/conncontroller/conncontroller.go:341

	}
	clientVersion := parts[1]
	expectedVersion := fmt.Sprintf("v%s", wavebase.WaveVersion)
	if semver.Compare(clientVersion, expectedVersion) < 0 {
		return false, clientVersion, "", nil
	}
	return true, clientVersion, "", nil
}

// for testing only -- trying to determine the env difference when attaching or not attaching a pty to an ssh session
func (conn *SSHConn) GetEnvironmentMaps(ctx context.Context) (map[string]string, map[string]string, error) {
	client := conn.GetClient()
	if client == nil {
		return nil, nil, fmt.Errorf("ssh client is not connected")
	}

	noPtyEnv, err := conn.getEnvironmentNoPty(ctx, client)
	if err != nil {
		return nil, nil, fmt.Errorf("error getting environment without PTY: %w", err)
	}

	ptyEnv, err := conn.getEnvironmentWithPty(ctx, client)
	if err != nil {
		return nil, nil, fmt.Errorf("error getting environment with PTY: %w", err)
	}

	return noPtyEnv, ptyEnv, nil
}

func runSessionWithContext(ctx context.Context, session *ssh.Session, cmd string) error {
	errCh := make(chan error, 1)

	go func() {
		errCh <- session.Run(cmd)
	}()

	select {

View on GitHub (pinned to a4447c1563)