diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings
index 4f69ceed1..871c9b2e4 100644
--- a/Telegram/Resources/langs/lang.strings
+++ b/Telegram/Resources/langs/lang.strings
@@ -1136,12 +1136,17 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 "lng_call_status_failed" = "failed to connect";
 "lng_call_status_ringing" = "ringing...";
 "lng_call_status_busy" = "line busy";
+"lng_call_fingerprint_tooltip" = "If emoji on {user}'s screen are the same, this call is 100% secure";
+
+"lng_call_error_not_available" = "Sorry, {user} doesn't accept calls.";
+"lng_call_error_incompatible" = "{user}'s app is using an incompatible protocol. They need to update their app before you can call them.";
+"lng_call_error_outdated" = "{user}'s app does not support calls. They need to update their app before you can call them.";
 
 "lng_call_bar_info" = "Show call info";
 "lng_call_bar_hangup" = "End call";
 
 "lng_call_box_title" = "Calls";
-"lng_call_box_about" = "Your history of Telegram calls will be here.";
+"lng_call_box_about" = "You didn't make any calls yet.";
 "lng_call_box_status_today" = "{time}";
 "lng_call_box_status_yesterday" = "Yesterday at {time}";
 "lng_call_box_status_date" = "{date} at {time}";
diff --git a/Telegram/SourceFiles/calls/calls_call.cpp b/Telegram/SourceFiles/calls/calls_call.cpp
index c1d6e2f5e..6f6e8ff09 100644
--- a/Telegram/SourceFiles/calls/calls_call.cpp
+++ b/Telegram/SourceFiles/calls/calls_call.cpp
@@ -22,6 +22,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 
 #include "auth_session.h"
 #include "mainwidget.h"
+#include "lang.h"
+#include "boxes/confirm_box.h"
 #include "calls/calls_instance.h"
 #include "base/openssl_help.h"
 #include "mtproto/connection.h"
@@ -143,7 +145,7 @@ void Call::startOutgoing() {
 		_accessHash = phoneCall.vaccess_hash.v;
 		handleUpdate(call.vphone_call);
 	}).fail([this](const RPCError &error) {
-		setState(State::Failed);
+		handleRequestError(error);
 	}).send();
 }
 
@@ -156,7 +158,7 @@ void Call::startIncoming() {
 			setState(State::WaitingIncoming);
 		}
 	}).fail([this](const RPCError &error) {
-		setState(State::Failed);
+		handleRequestError(error);
 	}).send();
 }
 
@@ -179,7 +181,7 @@ void Call::answer() {
 
 		handleUpdate(call.vphone_call);
 	}).fail([this](const RPCError &error) {
-		setState(State::Failed);
+		handleRequestError(error);
 	}).send();
 }
 
@@ -283,6 +285,9 @@ bool Call::handleUpdate(const MTPPhoneCall &call) {
 				MTP::send(MTPphone_SaveCallDebug(MTP_inputPhoneCall(MTP_long(_id), MTP_long(_accessHash)), MTP_dataJSON(MTP_string(debugLog))));
 			}
 		}
+		if (data.has_reason() && data.vreason.type() == mtpc_phoneCallDiscardReasonDisconnect) {
+			LOG(("Call Info: Discarded with DISCONNECT reason."));
+		}
 		if (data.has_reason() && data.vreason.type() == mtpc_phoneCallDiscardReasonBusy) {
 			setState(State::Busy);
 		} else {
@@ -334,7 +339,7 @@ void Call::confirmAcceptedCall(const MTPDphoneCallAccepted &call) {
 
 		createAndStartController(call.vphone_call.c_phoneCall());
 	}).fail([this](const RPCError &error) {
-		setState(State::Failed);
+		handleRequestError(error);
 	}).send();
 }
 
@@ -419,8 +424,9 @@ void Call::handleControllerStateChange(tgvoip::VoIPController *controller, int s
 	} break;
 
 	case STATE_FAILED: {
-		DEBUG_LOG(("Call Info: State changed to Failed."));
-		setStateQueued(State::Failed);
+		auto error = controller->GetLastError();
+		LOG(("Call Info: State changed to Failed, error: %1.").arg(error));
+		setFailedQueued(error);
 	} break;
 
 	default: LOG(("Call Error: Unexpected state in handleStateChange: %1").arg(state));
@@ -524,6 +530,28 @@ void Call::setStateQueued(State state) {
 	InvokeQueued(this, [this, state] { setState(state); });
 }
 
+void Call::setFailedQueued(int error) {
+	InvokeQueued(this, [this, error] { handleControllerError(error); });
+}
+
+void Call::handleRequestError(const RPCError &error) {
+	if (error.type() == qstr("USER_PRIVACY_RESTRICTED")) {
+		Ui::show(Box<InformBox>(lng_call_error_not_available(lt_user, App::peerName(_user))));
+	} else if (error.type() == qstr("PARTICIPANT_VERSION_OUTDATED")) {
+		Ui::show(Box<InformBox>(lng_call_error_outdated(lt_user, App::peerName(_user))));
+	} else if (error.type() == qstr("CALL_PROTOCOL_LAYER_INVALID")) {
+		Ui::show(Box<InformBox>(lng_call_error_incompatible(lt_user, App::peerName(_user))));
+	}
+	setState(State::Failed);
+}
+
+void Call::handleControllerError(int error) {
+	if (error == TGVOIP_ERROR_INCOMPATIBLE) {
+		Ui::show(Box<InformBox>(lng_call_error_incompatible(lt_user, App::peerName(_user))));
+	}
+	setState(State::Failed);
+}
+
 Call::~Call() {
 	if (_controller) {
 		DEBUG_LOG(("Call Info: Destroying call controller.."));
diff --git a/Telegram/SourceFiles/calls/calls_call.h b/Telegram/SourceFiles/calls/calls_call.h
index 8fa59b1d2..cbedd17fe 100644
--- a/Telegram/SourceFiles/calls/calls_call.h
+++ b/Telegram/SourceFiles/calls/calls_call.h
@@ -107,6 +107,8 @@ public:
 	~Call();
 
 private:
+	void handleRequestError(const RPCError &error);
+	void handleControllerError(int error);
 	void finish(const MTPPhoneCallDiscardReason &reason);
 	void startOutgoing();
 	void startIncoming();
@@ -124,6 +126,7 @@ private:
 	void startConfirmedCall(const MTPDphoneCall &call);
 	void setState(State state);
 	void setStateQueued(State state);
+	void setFailedQueued(int error);
 
 	gsl::not_null<Delegate*> _delegate;
 	gsl::not_null<UserData*> _user;
diff --git a/Telegram/SourceFiles/calls/calls_instance.cpp b/Telegram/SourceFiles/calls/calls_instance.cpp
index 9711db559..dbdf6d95d 100644
--- a/Telegram/SourceFiles/calls/calls_instance.cpp
+++ b/Telegram/SourceFiles/calls/calls_instance.cpp
@@ -22,6 +22,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
 
 #include "mtproto/connection.h"
 #include "auth_session.h"
+#include "apiwrap.h"
+#include "lang.h"
+#include "boxes/confirm_box.h"
 #include "calls/calls_call.h"
 #include "calls/calls_panel.h"
 
@@ -39,6 +42,12 @@ void Instance::startOutgoingCall(gsl::not_null<UserData*> user) {
 		_currentCallPanel->showAndActivate();
 		return; // Already in a call.
 	}
+	if (user->callsStatus() == UserData::CallsStatus::Private) {
+		// Request full user once more to refresh the setting in case it was changed.
+		AuthSession::Current().api().requestFullPeer(user);
+		Ui::show(Box<InformBox>(lng_call_error_not_available(lt_user, App::peerName(user))));
+		return;
+	}
 	createCall(user, Call::Type::Outgoing);
 }