diff --git a/ops/framework.py b/ops/framework.py
index f455bbd..f08845c 100755
--- a/ops/framework.py
+++ b/ops/framework.py
@@ -753,6 +753,31 @@ class Framework(Object):
         self._reemit()
 
     def _reemit(self, single_event_path=None):
+
+        class EventContext:
+            """Handles toggling the hook-is-running state in backends.
+
+            This allows e.g. harness logic to know if it is executing within a running hook context
+            or not.  It sets backend._hook_is_running equal to the name of the currently running
+            hook (e.g. "set-leader") and reverts back to the empty string when the hook execution
+            is completed.
+            """
+
+            def __init__(self, framework, event_name):
+                self._event = event_name
+                self._backend = None
+                if framework.model is not None:
+                    self._backend = framework.model._backend
+
+            def __enter__(self):
+                if self._backend:
+                    self._backend._hook_is_running = self._event
+                return self
+
+            def __exit__(self, exception_type, exception, traceback):
+                if self._backend:
+                    self._backend._hook_is_running = ''
+
         last_event_path = None
         deferred = True
         for event_path, observer_path, method_name in self._storage.notices(single_event_path):
@@ -779,15 +804,16 @@ class Framework(Object):
                 if custom_handler:
                     event_is_from_juju = isinstance(event, charm.HookEvent)
                     event_is_action = isinstance(event, charm.ActionEvent)
-                    if (
-                        event_is_from_juju or event_is_action
-                    ) and self._juju_debug_at.intersection({'all', 'hook'}):
-                        # Present the welcome message and run under PDB.
-                        self._show_debug_code_message()
-                        pdb.runcall(custom_handler, event)
-                    else:
-                        # Regular call to the registered method.
-                        custom_handler(event)
+                    with EventContext(self, event_handle.kind):
+                        if (
+                            event_is_from_juju or event_is_action
+                        ) and self._juju_debug_at.intersection({'all', 'hook'}):
+                            # Present the welcome message and run under PDB.
+                            self._show_debug_code_message()
+                            pdb.runcall(custom_handler, event)
+                        else:
+                            # Regular call to the registered method.
+                            custom_handler(event)
 
             if event.deferred:
                 deferred = True
diff --git a/ops/model.py b/ops/model.py
index 583c55b..388b18a 100644
--- a/ops/model.py
+++ b/ops/model.py
@@ -492,7 +492,9 @@ class RelationMapping(Mapping):
                 is_peer = relation_name in self._peers
                 return Relation(relation_name, relation_id, is_peer,
                                 self._our_unit, self._backend, self._cache)
-        num_related = len(self[relation_name])
+        relations = self[relation_name]
+        num_related = len(relations)
+        self._backend._validate_relation_access(relation_name, relations)
         if num_related == 0:
             return None
         elif num_related == 1:
@@ -1585,6 +1587,7 @@ class _ModelBackend:
 
         self._is_leader = None
         self._leader_check_time = None
+        self._hook_is_running = ''
 
     def _run(self, *args, return_output=False, use_json=False):
         kwargs = dict(stdout=PIPE, stderr=PIPE, check=True)
@@ -1609,6 +1612,14 @@ class _ModelBackend:
     def _is_relation_not_found(model_error):
         return 'relation not found' in str(model_error)
 
+    def _validate_relation_access(self, relation_name, relations):
+        """Checks for relation usage inconsistent with the framework/backend state.
+
+        This is used for catching Harness configuration errors and the production implementation
+        here should remain empty.
+        """
+        pass
+
     def relation_ids(self, relation_name):
         relation_ids = self._run('relation-ids', relation_name, return_output=True, use_json=True)
         return [int(relation_id.split(':')[-1]) for relation_id in relation_ids]

diff --git a/ops/testing.py b/ops/testing.py
index 335ae52..48ae58a 100755
--- a/ops/testing.py
+++ b/ops/testing.py
@@ -952,16 +952,19 @@ class Harness(typing.Generic[CharmType]):
     def set_leader(self, is_leader: bool = True) -> None:
         """Set whether this unit is the leader or not.
 
-        If this charm becomes a leader then `leader_elected` will be triggered.
+        If this charm becomes a leader then `leader_elected` will be triggered.  If Harness.begin()
+        has already been called, then the charm's peer relation should usually be added  *prior* to
+        calling this method (i.e. with Harness.add_relation) to properly initialize and make
+        available relation data that leader elected hooks may want to access.
 
         Args:
             is_leader: True/False as to whether this unit is the leader.
         """
-        was_leader = self._backend._is_leader
         self._backend._is_leader = is_leader
+
         # Note: jam 2020-03-01 currently is_leader is cached at the ModelBackend level, not in
         # the Model objects, so this automatically gets noticed.
-        if is_leader and not was_leader and self._charm is not None and self._hooks_enabled:
+        if is_leader and self._charm is not None and self._hooks_enabled:
             self._charm.on.leader_elected.emit()
 
     def set_planned_units(self, num_units: int) -> None:
@@ -1103,6 +1106,24 @@ class _TestingModelBackend:
         self._pebble_clients = {}  # type: {str: _TestingPebbleClient}
         self._pebble_clients_can_connect = {}  # type: {_TestingPebbleClient: bool}
         self._planned_units = None
+        self._hook_is_running = ''
+
+    def _validate_relation_access(self, relation_name, relations):
+        """Ensures that the named relation exists/has been added.
+
+        This is called whenever relation data is accessed via model.get_relation(...).
+        """
+        if len(relations) > 0:
+            return
+
+        relations = list(self._meta.peers.keys())
+        relations.extend(self._meta.requires.keys())
+        relations.extend(self._meta.provides.keys())
+        if self._hook_is_running == 'leader_elected' and relation_name in relations:
+            raise RuntimeError(
+                'cannot access relation data without first adding the relation: '
+                'use Harness.add_relation({!r}, <app>) before calling set_leader'
+                .format(relation_name))
 
     def _can_connect(self, pebble_client) -> bool:
         """Returns whether the mock client is active and can support API calls with no errors."""
diff --git a/test/test_testing.py b/test/test_testing.py
index 83c9333..795ea28 100644
--- a/test/test_testing.py
+++ b/test/test_testing.py
@@ -58,6 +58,20 @@ from ops.testing import (
 is_linux = platform.system() == 'Linux'
 
 
+class SetLeaderErrorTester(CharmBase):
+    """Sets peer relation data inside leader-elected."""
+
+    def __init__(self, framework):
+        super().__init__(framework)
+        self._peer_name = 'peer'
+        self.framework.observe(self.on.leader_elected,
+                               self._on_leader_elected)
+
+    def _on_leader_elected(self, event):
+        peers = self.model.get_relation(self._peer_name)
+        peers.data[self.app]["foo"] = "bar"
+
+
 class StorageTester(CharmBase):
     """Record the relation-changed events."""
 
@@ -791,6 +805,21 @@ class TestHarness(unittest.TestCase):
         self.assertEqual(rel.data[harness.charm.model.unit]['key'], 'v4')
         self.assertEqual([], helper.changes)
 
+    def test_harness_leader_misconfig(self):
+        # language=YAML
+        harness = Harness(SetLeaderErrorTester, meta='''
+            name: postgresql
+            peers:
+              peer:
+                interface: foo
+            ''')
+        self.addCleanup(harness.cleanup)
+        harness.begin()
+
+        with self.assertRaises(RuntimeError) as cm:
+            harness.set_leader(is_leader=True)
+        self.assertTrue(cm.exception.args[0].find('use Harness.add_relation') != -1)
+
     def test_update_peer_relation_app_data(self):
         # language=YAML
         harness = Harness(CharmBase, meta='''

`ops.harness` prohibits setting application data both in peer relation created and leader elected events
## Issue

Attached to this report is a simple charm (zip file) demonstrating a problem with `ops.harness`. This (workload-less) charm only has handlers for `RelationCreated` and `LeaderElected` events. The charm sets different peer application data fields in response each of these two events. The charm can be built and deployed without any error. Also the charm successfully sets both data items in peer relation created and leader elected events (as can be seen using juju show-unit). 

Also provided with the charm are two unit tests both of which fail. The only difference between these two unit tests is the order in which the peer relation created and leader elected events are executed. The tests fail because accessing/setting the peer application relation data fields fails. Note this failure is not a result of a missing leader guard which is implemented. 

PS: If there is an ordering between these two events it was not obvious from the [reference docs](https://ops.readthedocs.io/en/latest/). If there is an ordering it will be great to have this made explicit in the doc strings. Note peer relation created and relation created are qualitatively different events in that the former will always happen when a charm is deployed. This issue pertains to the peer relation created event. 


[harness-test.zip](https://github.com/canonical/operator/files/8098103/harness-test.zip)


test/test_testing.py::TestHarness::test_harness_leader_misconfig
test/test_testing.py::TestHarness::test_actions_from_directory test/test_testing.py::TestHarness::test_actions_passed_in test/test_testing.py::TestHarness::test_add_oci_resource_custom test/test_testing.py::TestHarness::test_add_oci_resource_no_image test/test_testing.py::TestHarness::test_add_peer_relation_with_initial_data_leader test/test_testing.py::TestHarness::test_add_relation test/test_testing.py::TestHarness::test_add_relation_and_unit test/test_testing.py::TestHarness::test_add_relation_with_our_initial_data test/test_testing.py::TestHarness::test_add_relation_with_remote_app_data test/test_testing.py::TestHarness::test_add_resource_but_oci test/test_testing.py::TestHarness::test_add_resource_bytes test/test_testing.py::TestHarness::test_add_resource_string test/test_testing.py::TestHarness::test_add_resource_unknown test/test_testing.py::TestHarness::test_add_resource_unknown_filename test/test_testing.py::TestHarness::test_add_storage_after_harness_begin test/test_testing.py::TestHarness::test_add_storage_before_harness_begin test/test_testing.py::TestHarness::test_add_storage_without_metadata_key_fails test/test_testing.py::TestHarness::test_app_status test/test_testing.py::TestHarness::test_attach_storage test/test_testing.py::TestHarness::test_attach_storage_before_harness_begin test/test_testing.py::TestHarness::test_begin_twice test/test_testing.py::TestHarness::test_begin_with_initial_hooks_install_sets_status test/test_testing.py::TestHarness::test_begin_with_initial_hooks_multiple_relation_same_endpoint test/test_testing.py::TestHarness::test_begin_with_initial_hooks_no_relations test/test_testing.py::TestHarness::test_begin_with_initial_hooks_no_relations_not_leader test/test_testing.py::TestHarness::test_begin_with_initial_hooks_peer_relation_pre_defined test/test_testing.py::TestHarness::test_begin_with_initial_hooks_relation_charm_with_no_relation test/test_testing.py::TestHarness::test_begin_with_initial_hooks_unknown_status test/test_testing.py::TestHarness::test_begin_with_initial_hooks_with_application_data test/test_testing.py::TestHarness::test_begin_with_initial_hooks_with_multiple_units test/test_testing.py::TestHarness::test_begin_with_initial_hooks_with_one_relation test/test_testing.py::TestHarness::test_begin_with_initial_hooks_with_peer_relation test/test_testing.py::TestHarness::test_can_connect test/test_testing.py::TestHarness::test_config_from_directory test/test_testing.py::TestHarness::test_container_isdir_and_exists test/test_testing.py::TestHarness::test_container_pebble_ready test/test_testing.py::TestHarness::test_create_harness_twice test/test_testing.py::TestHarness::test_detach_storage test/test_testing.py::TestHarness::test_detach_storage_before_harness_begin test/test_testing.py::TestHarness::test_empty_config_raises test/test_testing.py::TestHarness::test_get_backend_calls test/test_testing.py::TestHarness::test_get_backend_calls_with_kwargs test/test_testing.py::TestHarness::test_get_pebble_container_plan test/test_testing.py::TestHarness::test_get_pebble_container_plan_unknown test/test_testing.py::TestHarness::test_get_pod_spec test/test_testing.py::TestHarness::test_get_relation_data test/test_testing.py::TestHarness::test_hooks_disabled_contextmanager test/test_testing.py::TestHarness::test_hooks_disabled_nested_contextmanager test/test_testing.py::TestHarness::test_hooks_disabled_noop test/test_testing.py::TestHarness::test_hooks_enabled_and_disabled test/test_testing.py::TestHarness::test_metadata_from_directory test/test_testing.py::TestHarness::test_no_event_on_empty_update_relation_unit_app test/test_testing.py::TestHarness::test_no_event_on_empty_update_relation_unit_bag test/test_testing.py::TestHarness::test_no_event_on_no_diff_update_relation_unit_app test/test_testing.py::TestHarness::test_no_event_on_no_diff_update_relation_unit_bag test/test_testing.py::TestHarness::test_populate_oci_resources test/test_testing.py::TestHarness::test_relation_events test/test_testing.py::TestHarness::test_relation_set_app_not_leader test/test_testing.py::TestHarness::test_relation_set_deletes test/test_testing.py::TestHarness::test_remove_detached_storage test/test_testing.py::TestHarness::test_remove_relation test/test_testing.py::TestHarness::test_remove_relation_unit test/test_testing.py::TestHarness::test_remove_specific_relation_id test/test_testing.py::TestHarness::test_remove_storage_after_harness_begin test/test_testing.py::TestHarness::test_remove_storage_before_harness_begin test/test_testing.py::TestHarness::test_remove_storage_without_metadata_key_fails test/test_testing.py::TestHarness::test_removing_invalid_relation_id_raises_exception test/test_testing.py::TestHarness::test_removing_relation_refreshes_charm_model test/test_testing.py::TestHarness::test_removing_relation_removes_remote_app_data test/test_testing.py::TestHarness::test_removing_relation_unit_does_not_remove_other_unit_and_data test/test_testing.py::TestHarness::test_removing_relation_unit_removes_data_also test/test_testing.py::TestHarness::test_resource_folder_cleanup test/test_testing.py::TestHarness::test_set_leader test/test_testing.py::TestHarness::test_set_model_info_after_begin test/test_testing.py::TestHarness::test_set_model_name test/test_testing.py::TestHarness::test_set_model_name_after_begin test/test_testing.py::TestHarness::test_set_model_uuid_after_begin test/test_testing.py::TestHarness::test_set_workload_version test/test_testing.py::TestHarness::test_simulate_can_connect test/test_testing.py::TestHarness::test_storage_with_hyphens_works test/test_testing.py::TestHarness::test_unit_status test/test_testing.py::TestHarness::test_update_config test/test_testing.py::TestHarness::test_update_config_undefined_option test/test_testing.py::TestHarness::test_update_config_unset_boolean test/test_testing.py::TestHarness::test_update_peer_relation_app_data test/test_testing.py::TestHarness::test_update_peer_relation_no_local_unit_change_event test/test_testing.py::TestHarness::test_update_relation_exposes_new_data test/test_testing.py::TestHarness::test_update_relation_no_local_app_change_event test/test_testing.py::TestHarness::test_update_relation_no_local_unit_change_event test/test_testing.py::TestHarness::test_update_relation_remove_data test/test_testing.py::TestTestingModelBackend::test_conforms_to_model_backend test/test_testing.py::TestTestingModelBackend::test_get_pebble_methods test/test_testing.py::TestTestingModelBackend::test_lazy_resource_directory test/test_testing.py::TestTestingModelBackend::test_relation_get_unknown_relation_id test/test_testing.py::TestTestingModelBackend::test_relation_ids_unknown_relation test/test_testing.py::TestTestingModelBackend::test_relation_list_unknown_relation_id test/test_testing.py::TestTestingModelBackend::test_relation_remote_app_name test/test_testing.py::TestTestingModelBackend::test_resource_get_no_resource test/test_testing.py::TestTestingModelBackend::test_status_set_get_app test/test_testing.py::TestTestingModelBackend::test_status_set_get_unit test/test_testing.py::TestTestingPebbleClient::test_add_layer test/test_testing.py::TestTestingPebbleClient::test_add_layer_combine_no_override test/test_testing.py::TestTestingPebbleClient::test_add_layer_combine_override_merge test/test_testing.py::TestTestingPebbleClient::test_add_layer_combine_override_replace test/test_testing.py::TestTestingPebbleClient::test_add_layer_combine_override_unknown test/test_testing.py::TestTestingPebbleClient::test_add_layer_not_combined test/test_testing.py::TestTestingPebbleClient::test_add_layer_three_services test/test_testing.py::TestTestingPebbleClient::test_get_services_autostart test/test_testing.py::TestTestingPebbleClient::test_get_services_bad_request test/test_testing.py::TestTestingPebbleClient::test_get_services_none test/test_testing.py::TestTestingPebbleClient::test_get_services_not_started test/test_testing.py::TestTestingPebbleClient::test_get_services_start_stop test/test_testing.py::TestTestingPebbleClient::test_get_services_subset test/test_testing.py::TestTestingPebbleClient::test_get_services_unknown test/test_testing.py::TestTestingPebbleClient::test_invalid_start_service test/test_testing.py::TestTestingPebbleClient::test_methods_match_pebble_client test/test_testing.py::TestTestingPebbleClient::test_mixed_start_service test/test_testing.py::TestTestingPebbleClient::test_send_signal test/test_testing.py::TestTestingPebbleClient::test_start_service_str test/test_testing.py::TestTestingPebbleClient::test_start_started_service test/test_testing.py::TestTestingPebbleClient::test_stop_service_str test/test_testing.py::TestTestingPebbleClient::test_stop_services_unknown test/test_testing.py::TestTestingPebbleClient::test_stop_stopped_service test/test_testing.py::TestMockFilesystem::test_create_and_read_with_different_encodings test/test_testing.py::TestMockFilesystem::test_create_dir_with_extra_args test/test_testing.py::TestMockFilesystem::test_create_file_fails_if_parent_dir_doesnt_exist test/test_testing.py::TestMockFilesystem::test_create_file_from_bytes test/test_testing.py::TestMockFilesystem::test_create_file_from_files test/test_testing.py::TestMockFilesystem::test_create_file_from_str test/test_testing.py::TestMockFilesystem::test_create_file_succeeds_if_parent_dir_doesnt_exist_when_make_dirs_true test/test_testing.py::TestMockFilesystem::test_create_file_with_extra_args test/test_testing.py::TestMockFilesystem::test_delete_file test/test_testing.py::TestMockFilesystem::test_getattr test/test_testing.py::TestMockFilesystem::test_getattr_file_not_found test/test_testing.py::TestMockFilesystem::test_listdir test/test_testing.py::TestMockFilesystem::test_listdir_on_file test/test_testing.py::TestMockFilesystem::test_listdir_on_nonexistent_dir test/test_testing.py::TestMockFilesystem::test_listdir_root_on_empty_os test/test_testing.py::TestMockFilesystem::test_make_and_list_directory test/test_testing.py::TestMockFilesystem::test_make_directory_recursively test/test_testing.py::TestMockFilesystem::test_makedir test/test_testing.py::TestMockFilesystem::test_makedir_fails_if_already_exists test/test_testing.py::TestMockFilesystem::test_makedir_fails_if_parent_dir_doesnt_exist test/test_testing.py::TestMockFilesystem::test_makedir_path_must_start_with_slash test/test_testing.py::TestMockFilesystem::test_makedir_succeeds_if_already_exists_when_make_parents_true test/test_testing.py::TestMockFilesystem::test_open_directory_fails test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_list_directory_object_itself test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_list_files_not_found_raises test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_make_dir_with_ownership test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_make_dir_with_permission_mask test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_make_directory test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_make_directory_recursively test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_make_directory_with_relative_path_fails test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_make_subdir_of_file_fails test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_and_list_file test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_and_pull_bytes test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_and_pull_larger_file test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_and_pull_non_utf8_data test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_as_child_of_file_raises_error test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_file_with_relative_path_fails test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_files_and_list test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_files_and_list_by_pattern test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_to_non_existent_subdir test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_with_ownership test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_push_with_permission_mask test/test_testing.py::TestPebbleStorageAPIsUsingMocks::test_remove_path