gravitational/teleport · error

unexpected nil response from GetAssertion

Error message

unexpected nil response from GetAssertion

What it means

GetAssertion calls the Windows webauthn.dll entry point WebAuthNAuthenticatorGetAssertion, which reports success (ret == 0) but left the out *webauthnAssertion pointer nil. The Go wrapper treats this as an invariant violation: the Win32 API promised a result but produced none, so there is no assertion to parse and the call aborts with this plain error instead of dereferencing nil.

Source

Thrown at lib/auth/webauthnwin/webauthn_windows.go:141

	)

	if n.webauthnAPIVersion < int(in.opts.dwVersion) {
		const legacyVersion = 5
		in.opts.dwVersion = legacyVersion
		logger.DebugContext(context.Background(),
			"WebAuthn.dll too old, falling back to legacy version",
			"api_version", n.webauthnAPIVersion,
			"legacy_version", in.opts.dwVersion,
		)
	}

	var out *webauthnAssertion
	ret, err := webAuthNAuthenticatorGetAssertion(hwnd, in.rpID, in.clientData, in.opts, &out)
	if ret != 0 {
		return nil, trace.Wrap(getErrorNameOrLastErr(ret, err))
	}
	if out == nil {
		return nil, errors.New("unexpected nil response from GetAssertion")
	}

	// Note that we need to copy bytes out of `out` if we want to free object.
	// That's why bytesFromCBytes is used.
	defer freeAssertion(out)

	authData := bytesFromCBytes(out.cbAuthenticatorData, out.pbAuthenticatorData)
	signature := bytesFromCBytes(out.cbSignature, out.pbSignature)
	userID := bytesFromCBytes(out.cbUserID, out.pbUserID)
	credential := bytesFromCBytes(out.Credential.cbID, out.Credential.pbID)
	credType := windows.UTF16PtrToString(out.Credential.pwszCredentialType)

	return &wantypes.CredentialAssertionResponse{
		PublicKeyCredential: wantypes.PublicKeyCredential{
			RawID: credential,
			Credential: wantypes.Credential{
				ID:   base64.RawURLEncoding.EncodeToString(credential),
				Type: credType,

View on GitHub (pinned to 1283425b60)

Solutions

  1. Retry the WebAuthn ceremony once — transient Hello/TPM issues can clear the condition.
  2. Update Windows (webauthn.dll fixes ship in cumulative updates) and verify Windows Hello is set up and working in Settings > Accounts > Sign-in options.
  3. Fall back to another authenticator (hardware security key or non-native WebAuthn path) instead of the Windows-native implementation.
  4. Report to Teleport if reproducible on a supported Windows version; the code path expects a non-nil assertion on success.
Defensive patterns

Strategy: fallback

Validate before calling

// Before starting the ceremony, verify the native WebAuthn stack is usable:
if !native.HasCompileSupport() || !native.IsAvailable() {
    // fall back to non-native authenticator path
}

Type guard

func assertionAvailable(out *webauthnAssertion) bool { return out != nil }

Try / catch

resp, err := native.GetAssertion(origin, req)
if err != nil {
    if strings.Contains(err.Error(), "unexpected nil response") {
        // fall back to hardware key / retry once
    }
    return trace.Wrap(err)
}

Prevention

When it happens

Trigger: Calling nativeImpl.GetAssertion (used during WebAuthn login/ceremony in tsh and Teleport on Windows) when WebAuthNAuthenticatorGetAssertion returns HR_SUCCESS (0) but does not populate the WEBAUTHN_ASSERTION output struct — a defective or partially functional webauthn.dll / Windows Hello stack.

Common situations: Older or corrupted Windows builds with a buggy webauthn.dll; Windows Hello or the platform authenticator in a broken state (TPM faults, stale credentials); running on non-standard Windows editions or virtualized environments where the API succeeds spuriously.

Related errors


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