gravitational/teleport · info

unexpected message type

Error message

unexpected message type

What it means

Sentinel error returned when a TDP message read while waiting for MFA authentication messages is not one of the expected MFA types. Callers treat it as a signal to skip the message and keep reading, not a fatal failure.

Source

Thrown at lib/srv/desktop/tdp/mfa.go:31

//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package tdp

import (
	"context"
	"errors"
	"log/slog"

	"github.com/gravitational/trace"

	"github.com/gravitational/teleport/api/client/proto"
	"github.com/gravitational/teleport/api/mfa"
)

var (
	ErrUnexpectedTDPMessageType = errors.New("unexpected message type")
)

// convertChallenge converts an MFA challenge to a Message. Returns
// a non-nil error if the conversion fails
type convertChallenge func(*proto.MFAAuthenticateChallenge) (Message, error)

// asMFAResponse returns:
//   - ErrUnexpectedTDPMessageType if a valid messages was received but was not an MFA message.
//   - Any other non-nil error if there was an error interpreting the message.
//   - nil if a valid, non-nil MFA messages was found.
type asMFAResponse func(Message) (*proto.MFAAuthenticateResponse, error)

// NewMfaPrompt constructs a function that reads, encodes, and sends an MFA challenge to the client,
// then waits for the corresponding MFA response message. It caches any non-MFA messages received so
// that they may be forwarded to the server later on.
func NewMFAPrompt(rw MessageReadWriter, asResponse asMFAResponse, toMessage convertChallenge, withheld *[]Message, log *slog.Logger) mfa.PromptFunc {
	return func(ctx context.Context, chal *proto.MFAAuthenticateChallenge) (*proto.MFAAuthenticateResponse, error) {
		challengeMsg, err := toMessage(chal)

View on GitHub (pinned to 1283425b60)

Solutions

  1. No action needed in MFA read loops — match with errors.Is(err, ErrUnexpectedTDPMessageType) and continue reading
  2. If seen from legacy protocol/legacy/mfa.go default case unexpectedly, verify the peer's TDP protocol version
  3. Check for protocol desync (message stream corrupted or wrong wire format) if this error repeats abnormally

Example fix

// before
resp, err := asResponse(msg)
if err != nil { return nil, err }
// after
resp, err := asResponse(msg)
if errors.Is(err, tdp.ErrUnexpectedTDPMessageType) { continue }
if err != nil { return nil, err }
Defensive patterns

Strategy: try-catch

Try / catch

resp, err := asResponse(msg)
if errors.Is(err, tdp.ErrUnexpectedTDPMessageType) {
    continue // skip non-MFA message
}
if err != nil { return nil, trace.Wrap(err) }

Prevention

When it happens

Trigger: A non-MFA TDP message (e.g. mouse move, screen data) arrives while ReadMFAAuthenticateResponse-style code is waiting for an MFA challenge/response; also returned by legacy mfa.go converters in their default switch case.

Common situations: During RDP login with MFA, interleaved normal streaming messages arrive between MFA frames; protocol version mismatch causing unexpected message types.

Related errors


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