gravitational/teleport · info

disconnect escape sequence detected

Error message

disconnect escape sequence detected

What it means

ErrDisconnect is returned by lib/client/escape/Reader when the user types the OpenSSH-style disconnect escape sequence '~.' at the start of a line during an interactive session. It signals that the user requested the connection be interrupted; the reader sets it as its error and the session consumer should tear down the connection cleanly.

Source

Thrown at lib/client/escape/reader.go:40

import (
	"errors"
	"io"
	"sync"
)

const (
	readerBufferLimit = 10 * 1024 * 1024 // 10MB

	// Note: on a raw terminal, "\r\n" is needed to move a cursor to the start
	// of next line.
	helpText = "\r\ntsh escape characters:\r\n  ~? - display a list of escape characters\r\n  ~. - disconnect\r\n"
)

var (
	// ErrDisconnect is returned when the user has entered a disconnect
	// sequence, requesting connection to be interrupted.
	ErrDisconnect = errors.New("disconnect escape sequence detected")
	// ErrTooMuchBufferedData is returned when the Reader's internal buffer has
	// filled over 10MB. Either the consumer of Reader can't keep up with the
	// data or it's entirely stuck and not consuming the data.
	ErrTooMuchBufferedData = errors.New("internal buffer has grown too big")
)

// Reader is an io.Reader wrapper that catches OpenSSH-like escape sequences in
// the input stream. See NewReader for more info.
//
// Reader is safe for concurrent use.
type Reader struct {
	inner        io.Reader
	out          io.Writer
	onDisconnect func(error)
	bufferLimit  int

	// cond protects buf and err and also announces to blocked readers that
	// more data is available.

View on GitHub (pinned to 1283425b60)

Solutions

  1. Treat errors.Is(err, escape.ErrDisconnect) as an intentional, user-initiated disconnect — close the session without reporting a failure.
  2. If the user typed it accidentally, simply reconnect; nothing is corrupted.
  3. Use '~?' to display the escape help text and '~' twice to send a literal tilde.
  4. In automated consumers, propagate the disconnect error to the session teardown path rather than retrying.

Example fix

// before
_, err := io.Copy(sessionOut, escReader)
if err != nil {
    return trace.Wrap(err) // reports user disconnect as a failure
}
// after
_, err := io.Copy(sessionOut, escReader)
if errors.Is(err, escape.ErrDisconnect) {
    return nil // intentional user disconnect
}
if err != nil {
    return trace.Wrap(err)
}
Defensive patterns

Strategy: type-guard

Type guard

func IsDisconnect(err error) bool { return errors.Is(err, escape.ErrDisconnect) }

Try / catch

_, err := io.Copy(sessionOut, escReader)
if errors.Is(err, escape.ErrDisconnect) {
    return nil // user-initiated disconnect; close cleanly, no error
}
if err != nil {
    return trace.Wrap(err)
}

Prevention

When it happens

Trigger: reader.go:156 — the Reader buffered a '~' escape character and the next byte is '.', completing the disconnect sequence; this is delivered both as a read error and (as in reader_test.go:145) as the disconnect error to the session handler.

Common situations: Users pressing '~.' intending to type a tilde (e.g. in vim or a shell) at the beginning of a line during tsh SSH sessions; deliberate disconnection from a hung session.

Related errors


AI-assisted analysis of gravitational/teleport@1283425b60 (2026-09-02). Data as JSON: /api/errors/29faa550a5de6942. Report an issue: GitHub.