gravitational/teleport · warning

an unknown error has occurred

Error message

an unknown error has occurred

What it means

Fallback message produced by tdpHandshaker.sendError (lib/web/desktop.go:185). When the desktop access handshake needs to report an error to the client but the error value is nil (a bug — sendError was called with nothing to send), it substitutes the placeholder 'an unknown error has occurred', logs a warning, and writes a legacy Alert message to the client connection.

Source

Thrown at lib/web/desktop.go:185

		return trace.Wrap(err)
	}
	return trace.Wrap(w.conn.WriteMessage(websocket.BinaryMessage, data))
}

// implements handshaker for legacy TDP clients
// TODO(rhammonds) DELETE IN v20.0.0
type tdpHandshaker struct {
	connection tdp.MessageReadWriter
	withheld   []tdp.Message
	screenSpec legacy.ClientScreenSpec
	// May or may not be nil. Not all web client versions will send a keyboard layout.
	keyboardLayout *legacy.ClientKeyboardLayout
}

func (t *tdpHandshaker) sendError(ctx context.Context, log *slog.Logger, err error) error {
	if err == nil {
		log.WarnContext(ctx, "SendError called with empty message")
		err = errors.New("an unknown error has occurred")
	}

	return trace.Wrap(t.connection.WriteMessage(&legacy.Alert{
		Message:  err.Error(),
		Severity: legacy.SeverityError,
	}))
}

func (t *tdpHandshaker) getPromptBuilder(log *slog.Logger) mfaPromptBuilder {
	return legacy.NewTDPMFAPrompt(t.connection, &t.withheld, log)
}

func (t *tdpHandshaker) performInitialHandshake(ctx context.Context, log *slog.Logger) error {
	msg, err := t.connection.ReadMessage()
	if err != nil {
		return trace.Wrap(err)
	}

View on GitHub (pinned to 1283425b60)

Solutions

  1. Fix the caller so a non-nil error is always passed to sendError; find where the real error is discarded upstream.
  2. Check server logs for the 'SendError called with empty message' warning to locate the offending call site.
  3. Re-run the desktop session with debug logging to capture the actual handshake failure before it is replaced.

Example fix

// before
if someCondition {
    return hs.sendError(ctx, log, err) // err may be nil
}
// after
if someCondition {
    if err == nil {
        err = trace.Errorf("handshake failed")
    }
    return hs.sendError(ctx, log, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// never pass a nil error to sendError
if err == nil {
    err = trace.Errorf("desktop handshake failed")
}

Type guard

func nonNilErr(err error) error { if err == nil { return trace.Errorf("unknown handshake failure") }; return err }

Try / catch

if err := hs.handshake(ctx); err != nil {
    return hs.sendError(ctx, log, nonNilErr(err))
}

Prevention

When it happens

Trigger: Calling sendError on tdpHandshaker with err == nil during the TDP desktop handshake; usually caused by a code path swallowing the real error before reaching sendError.

Common situations: Desktop access sessions failing to handshake where the underlying error was lost (nil return from an intermediate step); misused API in custom integrations of the desktop handshake code.

Related errors


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