diff --git a/resources/static/dialog/controllers/dialog_controller.js b/resources/static/dialog/controllers/dialog_controller.js
index 6a16e5af9f9a6f76bbc9d23982b07304e60ca208..88fede3a584dc58aa186569188c3b234d146880e 100644
--- a/resources/static/dialog/controllers/dialog_controller.js
+++ b/resources/static/dialog/controllers/dialog_controller.js
@@ -65,8 +65,14 @@
         self.onsuccess = null;
         self.onerror = null;
 
-        win.setupChannel(self);
-        self.stateMachine();
+        try {
+          win.setupChannel(self);
+          self.stateMachine();
+        } catch (e) {
+          self.renderError("error.ejs", {
+            action: errors.relaySetup
+          });
+        }
       },
         
       getVerifiedEmail: function(origin_url, onsuccess, onerror) {
diff --git a/resources/static/dialog/resources/channel.js b/resources/static/dialog/resources/channel.js
index eb235bd41bab1dd453d4de17eb167e2325956637..859d641b8b266a4bb6a9c5719c1cc3af5b893030 100644
--- a/resources/static/dialog/resources/channel.js
+++ b/resources/static/dialog/resources/channel.js
@@ -1,5 +1,5 @@
 /*jshint browsers:true, forin: true, laxbreak: true */
-/*global alert:true, setupNativeChannel:true, setupIFrameChannel:true*/
+/*global BrowserID: true*/
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
@@ -68,7 +68,7 @@
 
   function setupNativeChannel(controller) {
     nav.id.channel.registerController(controller);
-  };
+  }
 
   function setupIFrameChannel(controller) {
     // TODO - Add a check for whether the dialog was opened by another window
@@ -91,17 +91,19 @@
         onCompleteCallback = onComplete;
         controller.getVerifiedEmail(origin, onsuccess, onerror);
       });
+      win.location.hash = '';
     }
-
-    win.location.hash = '';
-  };
+    else {
+      throw "relay frame not found";
+    }
+  }
 
   function open(controller) {
     if (nav.id && nav.id.channel)
       setupNativeChannel(controller);
     else
       setupIFrameChannel(controller);
-  };
+  }
 
 
   function init(options) {
@@ -125,9 +127,12 @@
        * @method init
        */
       init: init,
+
       /**
        * Open the channel.
        * @method open 
+       * @param {object} options - contains:
+       * *   options.getVerifiedEmail {function} - function to /get
        */
       open: open
     };
diff --git a/resources/static/dialog/resources/error-messages.js b/resources/static/dialog/resources/error-messages.js
index a35b91a18c869844e0aae0fbb8f186b1b402a7c7..3f6aeefc37aaea3b8209523edef1df81bd182377 100644
--- a/resources/static/dialog/resources/error-messages.js
+++ b/resources/static/dialog/resources/error-messages.js
@@ -1,3 +1,4 @@
+/*global BrowserID: true*/
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
@@ -73,6 +74,11 @@ BrowserID.Errors = (function(){
       title: "Registration Failed"
     },
 
+    relaySetup: {
+      title: "Establishing Relay",
+      message: "Relay frame could not be found"
+    },
+
     requestPasswordReset: {
       title: "Resetting Password"
     },
diff --git a/resources/static/dialog/test/qunit/controllers/dialog_controller_unit_test.js b/resources/static/dialog/test/qunit/controllers/dialog_controller_unit_test.js
index de0ce24219bcbf41bc3b679e3cc0924d90c6dc79..47be43214a20d1386b239295b1d27d35b7305928 100644
--- a/resources/static/dialog/test/qunit/controllers/dialog_controller_unit_test.js
+++ b/resources/static/dialog/test/qunit/controllers/dialog_controller_unit_test.js
@@ -37,24 +37,33 @@
 steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/controllers/dialog_controller", function() {
   "use strict";
 
-  var controller, el
+  var controller,
+      el,
+      channelError = false;
 
   function reset() {
     el = $("#controller_head");
     el.find("#formWrap .contents").html("");
     el.find("#wait .contents").html("");
     el.find("#error .contents").html("");
+
+    channelError = false;
+  }
+
+  function initController() {
+    controller = el.dialog({
+      window: {
+        setupChannel: function() { 
+          if (channelError) throw "Channel error";  
+        }
+      }
+    }).controller();
   }
 
   module("controllers/dialog_controller", {
     setup: function() {
       reset();
-
-      controller = el.dialog({
-        window: {
-          setupChannel: function() {}
-        }
-      }).controller();
+      initController();
     },
 
     teardown: function() {
@@ -63,17 +72,27 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/con
     } 
   });
 
+  test("initialization with channel error", function() {
+    controller.destroy();
+    reset();
+    channelError = true;
+
+    initController();
+
+    ok($("#error .contents").text().length, "contents have been written");
+  });
+
   test("doOffline", function() {
     controller.doOffline();
-    ok($("#error .contents").length, "contents have been written");
-    ok($("#error #offline").length, "offline error message has been written");
+    ok($("#error .contents").text().length, "contents have been written");
+    ok($("#error #offline").text().length, "offline error message has been written");
   });
 
   test("doXHRError while online, no network info given", function() {
     controller.doXHRError();
-    ok($("#error .contents").length, "contents have been written");
-    ok($("#error #action").length, "action contents have been written");
-    equal($("#error #network").length, 0, "no network contents to be written");
+    ok($("#error .contents").text().length, "contents have been written");
+    ok($("#error #action").text().length, "action contents have been written");
+    equal($("#error #network").text().length, 0, "no network contents to be written");
   });
 
   test("doXHRError while online, network info given", function() {
@@ -83,9 +102,9 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/con
         url: "browserid.org/verify"
       }
     });
-    ok($("#error .contents").length, "contents have been written");
-    ok($("#error #action").length, "action contents have been written");
-    ok($("#error #network").length, "network contents have been written");
+    ok($("#error .contents").text().length, "contents have been written");
+    ok($("#error #action").text().length, "action contents have been written");
+    ok($("#error #network").text().length, "network contents have been written");
   });
 
   test("doXHRError while offline does not update contents", function() {
@@ -93,7 +112,7 @@ steal.plugins("jquery").then("/dialog/controllers/page_controller", "/dialog/con
     $("#error #action").remove();
 
     controller.doXHRError();
-    ok(!$("#error #action").length, "XHR error is not reported if the user is offline.");
+    ok(!$("#error #action").text().length, "XHR error is not reported if the user is offline.");
   });
 
   test("window.unload causes setStagedOnBehalfOf data to be cleared", function() {
diff --git a/resources/static/dialog/test/qunit/resources/channel_unit_test.js b/resources/static/dialog/test/qunit/resources/channel_unit_test.js
index da72df2344699d499d668ad582b1640bd98c6c5a..cd9621638a5b131270a3296f3455fd7b18fa7f1b 100644
--- a/resources/static/dialog/test/qunit/resources/channel_unit_test.js
+++ b/resources/static/dialog/test/qunit/resources/channel_unit_test.js
@@ -116,7 +116,7 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/channel", func
     stop();
   });
 
-  test("IFRAME channel with error", function() {
+  test("IFRAME channel relaying error", function() {
     channel.init({
       window: winMock,
       navigator: navMock
@@ -133,5 +133,30 @@ steal.plugins("jquery", "funcunit/qunit").then("/dialog/resources/channel", func
     stop();
   });
 
+  test("IFRAME channel with error on open", function() {
+    var winMockWithoutRelay = $.extend(true, {}, winMock);
+    delete winMockWithoutRelay.opener.frames.browserid_relay_1234; 
+
+    channel.init({
+      window: winMockWithoutRelay,
+      navigator: navMock
+    });
+
+    // Do this manually so we can test if getVerifiedEmail gets called.
+    try {
+      channel.open({
+        getVerifiedEmail: function(origin, onsuccess, onerror) {
+          ok(false, "getVerifiedEmail should never be called on channel error");
+          start();
+        }
+      });
+    } catch(e) {
+      equal(e.toString(), "relay frame not found", "exception caught when trying to open channel that does not exist");
+      start();
+    }
+
+    stop();
+  });
+
 });