diff --git a/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py b/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py
index 2b68de96458c95d1cd2e068f7916dc9851ff3a45..550efab2b7be2627304ce24c24f4cf95cd5cb9c0 100644
--- a/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py
+++ b/SAS/TMSS/backend/services/scheduling/lib/constraints/template_constraints_v1.py
@@ -283,7 +283,9 @@ def can_run_anywhere_within_timewindow_with_sky_constraints(scheduling_unit: mod
                     target_rise_and_set_times = coordinates_timestamps_and_stations_to_target_rise_and_set(angle1=angle1, angle2=angle2, direction_type=direction_type, timestamps=timestamps, stations=tuple(stations), angle_to_horizon=min_elevation)
                     for station, times in target_rise_and_set_times.items():
                         for i in range(len(timestamps)):
-                            if not (timestamps[i] > times[0]['rise'] and timestamps[i] < times[0]['set']):
+                            if times[0]['always_above_horizon']:
+                                continue
+                            if times[0]['always_below_horizon'] or not (timestamps[i] > times[0]['rise'] and timestamps[i] < times[0]['set']):
                                 if task['specifications_template'] == 'calibrator observation':
                                     logger.info('min_calibrator_elevation=%s constraint is not met at timestamp=%s' % (min_elevation.rad, timestamps[i]))
                                 else:
diff --git a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
index 98c1c41ea5d494bcff75f928fc2da1216777ff51..e8fadb2c6085117007f7913c8ecee0fa3808b434 100755
--- a/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
+++ b/SAS/TMSS/backend/services/scheduling/test/t_dynamic_scheduling.py
@@ -735,8 +735,11 @@ class TestSkyConstraints(unittest.TestCase):
         self.distance_mock.return_value = self.distance_data
         self.addCleanup(self.distance_patcher.stop)
         
-        self.target_rise_and_set_data = {"CS002": [{"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0)},
-                                                   {"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0)}]}
+        self.target_rise_and_set_data = {"CS002": [{"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0), "always_above_horizon": False, "always_below_horizon": False},
+                                                   {"rise": datetime(2020, 1, 1, 8, 0, 0), "set": datetime(2020, 1, 1, 12, 30, 0), "always_above_horizon": False, "always_below_horizon": False}]}
+        self.target_rise_and_set_data_always_above = {"CS002": [{"rise": None, "set": None, "always_above_horizon": True, "always_below_horizon": False}]}
+        self.target_rise_and_set_data_always_below = {"CS002": [{"rise": None, "set": None, "always_above_horizon": False, "always_below_horizon": True}]}
+        
         self.target_rise_and_set_patcher = mock.patch('lofar.sas.tmss.services.scheduling.constraints.template_constraints_v1.coordinates_timestamps_and_stations_to_target_rise_and_set')
         self.target_rise_and_set_mock = self.target_rise_and_set_patcher.start()
         self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data
@@ -760,21 +763,45 @@ class TestSkyConstraints(unittest.TestCase):
         
     # min_target_elevation
 
-    def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_true_when_met(self):
+    def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_true(self):
+        self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data
+
         self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1}
         self.scheduling_unit_blueprint.save()
-        timestamp = datetime(2020, 1, 1, 10, 0, 0)
+        timestamp = datetime(2020, 1, 1, 10, 0, 0)  # target sets after obs ends (mocked response)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
         self.assertTrue(returned_value)
 
-    def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_false_when_not_met(self):
-        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.2}
+    def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_when_target_always_above_returns_true(self):
+        self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data_always_above
+
+        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1}
         self.scheduling_unit_blueprint.save()
-        timestamp = datetime(2020, 1, 1, 11, 0, 0)
+        timestamp = datetime(2020, 1, 1, 10, 0, 0)  # target is always up (mocked response)
+        returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
+        self.assertTrue(returned_value)
+
+    def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_constraint_returns_false(self):
+        self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data
+
+        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1}
+        self.scheduling_unit_blueprint.save()
+        timestamp = datetime(2020, 1, 1, 11, 0, 0)  # target sets before obs ends (mocked response)
+        returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
+        self.assertFalse(returned_value)
+
+    def test_can_run_anywhere_within_timewindow_with_sky_constraints_with_min_target_elevation_when_target_is_always_below_returns_false(self):
+        self.target_rise_and_set_mock.return_value = self.target_rise_and_set_data_always_below
+
+        self.scheduling_unit_blueprint.draft.scheduling_constraints_doc['sky'] = {'min_target_elevation': 0.1}
+        self.scheduling_unit_blueprint.save()
+        timestamp = datetime(2020, 1, 1, 10, 0, 0)  # target is never up (mocked response)
         returned_value = tc1.can_run_anywhere_within_timewindow_with_sky_constraints(self.scheduling_unit_blueprint, timestamp, timestamp + timedelta(seconds=self.obs_duration))
         self.assertFalse(returned_value)
 
 
+
+
 class TestTimeConstraints(TestCase):
     """
     Tests for the time constraint checkers used in dynamic scheduling with different boundaries
diff --git a/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py b/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py
index ae926e172f4a39a4ff77a442346fbf25d4505e35..3c0e184ce79ac8e697043dcf8ced5dceba3bf1eb 100644
--- a/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py
+++ b/SAS/TMSS/backend/src/tmss/tmssapp/conversions.py
@@ -126,9 +126,10 @@ def coordinates_timestamps_and_stations_to_target_rise_and_set(angle1: float, an
     :param stations: tuple of station names, e.g. ("CS002",)
     :param angle_to_horizon: the angle between horizon and given coordinates for which rise and set times are returned
     :return A dict that maps station names to a list of dicts with rise and set times for each requested date.
+            If rise and set are None, the target is always above or below horizon, and the respective boolean is True.
         E.g.
-        {"CS002": [{"rise": datetime(2020, 1, 1, 4, 0, 0), "set": datetime(2020, 1, 1, 11, 0, 0)},
-                   {"rise": datetime(2020, 1, 2, 4, 0, 0), "set": datetime(2020, 1, 2, 11, 0, 0)}]
+        {"CS002": [{"rise": datetime(2020, 1, 1, 4, 0, 0), "set": datetime(2020, 1, 1, 11, 0, 0), "always_above_horizon": False, "always_below_horizon": False},
+                   {"rise": datetime(2020, 1, 2, 4, 0, 0), "set": datetime(2020, 1, 2, 11, 0, 0), "always_above_horizon": False, "always_below_horizon": False}]
         }
     """
     if direction_type == "J2000":
@@ -140,10 +141,29 @@ def coordinates_timestamps_and_stations_to_target_rise_and_set(angle1: float, an
         for timestamp in timestamps:
             # todo: this can probably be made faster by moving the following logic to an own function with single station/timestamp as input and putting the lru_cache on there.
             observer = create_astroplan_observer_for_station(station)
-            target_set = observer.target_set_time(target=coord, time=Time(timestamp), horizon=angle_to_horizon, which='next', n_grid_points=TARGET_SET_RISE_PRECISION)
-            target_rise = observer.target_rise_time(target=coord, time=Time(target_set), horizon=angle_to_horizon, which='previous', n_grid_points=TARGET_SET_RISE_PRECISION)
+            try:
+                target_set = observer.target_set_time(target=coord, time=Time(timestamp), horizon=angle_to_horizon, which='next', n_grid_points=TARGET_SET_RISE_PRECISION)
+                target_rise = observer.target_rise_time(target=coord, time=Time(target_set), horizon=angle_to_horizon, which='previous', n_grid_points=TARGET_SET_RISE_PRECISION)
+                return_dict.setdefault(station, []).append(
+                    {"rise": target_rise.to_datetime(),
+                     "set": target_set.to_datetime(),
+                     "always_above_horizon": False,
+                     "always_below_horizon": False})
+            except TypeError as e:
+                if "numpy.float64" in str(e):
+                    # Note: when the target is always above or below horizon, astroplan excepts with the not very
+                    #  meaningful error: 'numpy.float64' object does not support item assignment
+                    # Determine whether the target is always above or below horizon so that we can return some useful
+                    # additional info, e.g. for scheduling purposes.
+                    is_up = observer.target_is_up(target=coord, time=Time(timestamp), horizon=angle_to_horizon)
+                    return_dict.setdefault(station, []).append(
+                        {"rise": None,
+                         "set": None,
+                         "always_above_horizon": is_up,
+                         "always_below_horizon": not is_up})
+                else:
+                    raise
 
-            return_dict.setdefault(station, []).append({"rise": target_rise.to_datetime(), "set": target_set.to_datetime()})
 
     return return_dict
 
diff --git a/SAS/TMSS/backend/test/t_conversions.py b/SAS/TMSS/backend/test/t_conversions.py
index 7f8d66d6e4b8758b3cf13bf04bf3d8488deb89ad..1773168c7b1ded14c41aee27f0fddd6683d9f9f7 100755
--- a/SAS/TMSS/backend/test/t_conversions.py
+++ b/SAS/TMSS/backend/test/t_conversions.py
@@ -362,6 +362,35 @@ class UtilREST(unittest.TestCase):
                 self.assertNotEqual(rise, rise_last)
             rise_last = rise
 
+    def test_util_target_rise_and_set_detects_when_target_above_horizon(self):
+
+        # assert always below and always above are usually false
+        r = requests.get(BASE_URL + '/util/target_rise_and_set?angle1=0.5&angle2=0.8&timestamps=2020-01-01&horizon=0.2', auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+        self.assertIsNotNone(r_dict['CS002'][0]['rise'])
+        self.assertIsNotNone(r_dict['CS002'][0]['set'])
+        self.assertFalse(r_dict['CS002'][0]['always_below_horizon'])
+        self.assertFalse(r_dict['CS002'][0]['always_above_horizon'])
+
+        # assert rise and set are None and flag is true when target is always above horizon
+        r = requests.get(BASE_URL + '/util/target_rise_and_set?angle1=0.5&angle2=0.8&timestamps=2020-01-01&horizon=0.1', auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+        self.assertIsNone(r_dict['CS002'][0]['rise'])
+        self.assertIsNone(r_dict['CS002'][0]['set'])
+        self.assertTrue(r_dict['CS002'][0]['always_above_horizon'])
+        self.assertFalse(r_dict['CS002'][0]['always_below_horizon'])
+
+        # assert rise and set are None and flag is true when target is always below horizon
+        r = requests.get(BASE_URL + '/util/target_rise_and_set?angle1=0.5&angle2=-0.5&timestamps=2020-01-01&horizon=0.2', auth=AUTH)
+        self.assertEqual(r.status_code, 200)
+        r_dict = json.loads(r.content.decode('utf-8'))
+        self.assertIsNone(r_dict['CS002'][0]['rise'])
+        self.assertIsNone(r_dict['CS002'][0]['set'])
+        self.assertFalse(r_dict['CS002'][0]['always_above_horizon'])
+        self.assertTrue(r_dict['CS002'][0]['always_below_horizon'])
+
 
 if __name__ == "__main__":
     os.environ['TZ'] = 'UTC'