diff --git a/Changelog.md b/Changelog.md
index 9d26fb9a9718203cfaf76933c772e035d04b8506..7300e570c30194f361316b08700133eefa8354fd 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,7 +1,10 @@
 # Changelog
 
+# [3.4] - 2025-05-08
+- Fix some bugs in the nightly validation schedule.
+
 # [3.3] - 2025-05-07
-- Fix import L3 core services bug
+- Fix CLI bug for importing L3 core services.
 
 # [3.2] - 2025-05-02
 - Allow running the Edge Port modification workflow on Juniper routers.
diff --git a/gso/schedules/validate_products.py b/gso/schedules/validate_products.py
index f51f5c22b6c3fb33bafb9065e34d494215834276..efc441ad21f3a4894b82a030e99fd7f0653e0a99 100644
--- a/gso/schedules/validate_products.py
+++ b/gso/schedules/validate_products.py
@@ -11,5 +11,5 @@ from gso.services.processes import count_incomplete_validate_products
 @scheduler(CronScheduleConfig(name="Validate Products and inactive subscriptions", minute="30", hour="2"))
 def validate_products() -> None:
     """Validate all products."""
-    if count_incomplete_validate_products() > 0:
+    if count_incomplete_validate_products() == 0:
         start_process("task_validate_geant_products")
diff --git a/gso/schedules/validate_subscriptions.py b/gso/schedules/validate_subscriptions.py
index 0ac4e36f655fda58f108967095123ef17f4f90dc..7323b6086b948201da4a0f5b0ee1574030646076 100644
--- a/gso/schedules/validate_subscriptions.py
+++ b/gso/schedules/validate_subscriptions.py
@@ -10,11 +10,11 @@ From this list, each workflow is selected that meets the following:
 import structlog
 from celery import shared_task
 from orchestrator.services.processes import get_execution_context
-from orchestrator.services.subscriptions import TARGET_DEFAULT_USABLE_MAP
+from orchestrator.services.subscriptions import TARGET_DEFAULT_USABLE_MAP, WF_USABLE_WHILE_OUT_OF_SYNC
 from orchestrator.targets import Target
 
 from gso.schedules.scheduling import CronScheduleConfig, scheduler
-from gso.services.subscriptions import get_active_insync_subscriptions
+from gso.services.subscriptions import get_active_subscriptions
 
 logger = structlog.get_logger(__name__)
 
@@ -22,8 +22,16 @@ logger = structlog.get_logger(__name__)
 @shared_task
 @scheduler(CronScheduleConfig(name="Subscriptions Validator", minute="10", hour="3"))
 def validate_subscriptions() -> None:
-    """Validate all subscriptions using their corresponding validation workflow."""
-    subscriptions = get_active_insync_subscriptions()
+    """Validate all subscriptions using their corresponding validation workflow.
+
+    Validation workflows only run on subscriptions that are active, even when they could be run on provisioning
+    subscriptions. E.g. for routers, they can manually be validated when provisioning, but are not included in this
+    schedule.
+
+    Validation workflows will, in principle, only run on subscriptions that are in sync. Except when a specific
+    validation workflow is marked as usable while out of sync in the list `WF_USABLE_WHILE_OUT_OF_SYNC`.
+    """
+    subscriptions = get_active_subscriptions()
     if not subscriptions:
         logger.info("No subscriptions to validate")
         return
@@ -35,18 +43,18 @@ def validate_subscriptions() -> None:
             if workflow.target == Target.SYSTEM and workflow.name.startswith("validate_"):
                 validation_workflow = workflow.name
 
-        if validation_workflow:
-            #  Validation workflows only run on subscriptions that are active, even when they could be run on
-            #  provisioning subscriptions. E.g. for routers, they can manually be validated when provisioning, but are
-            #  not included in this schedule.
-            usable_when = TARGET_DEFAULT_USABLE_MAP[Target.SYSTEM]
+            if validation_workflow:
+                validation_workflow_usable = (subscription.status in TARGET_DEFAULT_USABLE_MAP[Target.SYSTEM]) and (
+                    subscription.insync or (workflow in WF_USABLE_WHILE_OUT_OF_SYNC)
+                )
+
+                if validation_workflow_usable:
+                    json = [{"subscription_id": str(subscription.subscription_id)}]
 
-            if subscription.status in usable_when:
-                json = [{"subscription_id": str(subscription.subscription_id)}]
+                    validate_func = get_execution_context()["validate"]
+                    validate_func(validation_workflow, json=json)
 
-                validate_func = get_execution_context()["validate"]
-                validate_func(validation_workflow, json=json)
-        else:
+        if not validation_workflow:
             logger.warning(
                 "SubscriptionTable has no validation workflow",
                 subscription=subscription,
diff --git a/gso/services/subscriptions.py b/gso/services/subscriptions.py
index 6ae731410e60b1a60e0a80b3b5cd11508e101aab..93e849d892144299f932467501e8d82b1805ef89 100644
--- a/gso/services/subscriptions.py
+++ b/gso/services/subscriptions.py
@@ -343,11 +343,11 @@ def get_subscription_by_process_id(process_id: str) -> SubscriptionModel | None:
     return SubscriptionModel.from_subscription(subscription_table.subscription_id) if subscription_table else None
 
 
-def get_active_insync_subscriptions() -> list[SubscriptionTable]:
-    """Retrieve all subscriptions that are currently active and in sync."""
+def get_active_subscriptions() -> list[SubscriptionTable]:
+    """Retrieve all subscriptions that are currently active."""
     return (
         SubscriptionTable.query.join(ProductTable)
-        .filter(SubscriptionTable.insync.is_(True), SubscriptionTable.status == SubscriptionLifecycle.ACTIVE.value)
+        .filter(SubscriptionTable.status == SubscriptionLifecycle.ACTIVE)
         .all()
     )
 
diff --git a/gso/workflows/l3_core_service/geant_ip/validate_prefix_list.py b/gso/workflows/l3_core_service/geant_ip/validate_prefix_list.py
index ec335abd7aa3f70ac054a5d6d518893c97a4a548..8dc838d20f7c29db62359d8cda6bb5a2538ef320 100644
--- a/gso/workflows/l3_core_service/geant_ip/validate_prefix_list.py
+++ b/gso/workflows/l3_core_service/geant_ip/validate_prefix_list.py
@@ -7,7 +7,7 @@ from orchestrator.domain import SubscriptionModel
 from orchestrator.forms import SubmitFormPage
 from orchestrator.targets import Target
 from orchestrator.workflow import StepList, begin, conditional, done, inputstep, step, workflow
-from orchestrator.workflows.steps import resync, store_process_subscription, unsync
+from orchestrator.workflows.steps import resync, store_process_subscription, unsync_unchecked
 from orchestrator.workflows.utils import wrap_modify_initial_input_form
 from pydantic import Field
 from pydantic_forms.types import FormGenerator, State, UUIDstr
@@ -26,7 +26,7 @@ def build_fqdn_list(subscription_id: UUIDstr) -> State:
     ap_fqdn_list = [
         ap.sbp.edge_port.node.router_fqdn for ap in ap_list if ap.sbp.edge_port.node.vendor != Vendor.JUNIPER
     ]
-    return {"ap_fqdn_list": ap_fqdn_list, "subscription": subscription}
+    return {"ap_fqdn_list": ap_fqdn_list, "subscription": subscription, "subscription_was_in_sync": subscription.insync}
 
 
 @step("[DRY RUN] Validate Prefix-Lists")
@@ -115,14 +115,15 @@ def validate_geant_ip_prefix_list() -> StepList:
     """Validate prefix-lists for an existing GÉANT IP subscription."""
     fqdn_list_is_empty = conditional(lambda state: state["ap_fqdn_list"] == [])
     prefix_list_has_drifted = conditional(lambda state: bool(state["prefix_list_drift"]))
+    subscription_was_in_sync = conditional(lambda state: bool(state["subscription_was_in_sync"]))
 
     redeploy_prefix_list_steps = (
         begin
-        >> unsync
+        >> unsync_unchecked
         >> await_operator
         >> lso_interaction(deploy_prefix_lists_dry)
         >> lso_interaction(deploy_prefix_lists_real)
-        >> resync
+        >> subscription_was_in_sync(resync)
     )
     prefix_list_validation_steps = (
         begin
diff --git a/gso/workflows/tasks/send_email_notifications.py b/gso/workflows/tasks/send_email_notifications.py
index cf87864bb51d724ead84e31663fb754c1a3946b6..6db502a7932c381d273ef8c863a22e71165edd4f 100644
--- a/gso/workflows/tasks/send_email_notifications.py
+++ b/gso/workflows/tasks/send_email_notifications.py
@@ -18,7 +18,7 @@ from gso.settings import load_oss_params
 @step("Gather all tasks that have failed")
 def gather_failed_tasks() -> State:
     """Gather all tasks that have failed."""
-    failed_prefix_list_tasks = get_suspended_tasks_by_workflow_name("validate_prefix_list")
+    failed_prefix_list_tasks = get_suspended_tasks_by_workflow_name("validate_geant_ip_prefix_list")
     all_other_tasks = list(set(get_failed_tasks()) - set(failed_prefix_list_tasks))
 
     return {
diff --git a/setup.py b/setup.py
index 8e83d4c1e7cbab699f2fc7faf9cc0223a93d8b4c..58344ee060832df1d14ea56f7d86e56d1ab824a0 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@ from setuptools import find_packages, setup
 
 setup(
     name="geant-service-orchestrator",
-    version="3.3",
+    version="3.4",
     author="GÉANT Orchestration and Automation Team",
     author_email="goat@geant.org",
     description="GÉANT Service Orchestrator",
diff --git a/test/schedules/test_scheduling.py b/test/schedules/test_scheduling.py
index 93f1b5d3326719c4faecc3485483402a848a0875..a1eb56b48a4d0023c527635701ba85dbfa5a4bab 100644
--- a/test/schedules/test_scheduling.py
+++ b/test/schedules/test_scheduling.py
@@ -14,8 +14,8 @@ def validate_subscriptions():
 
 
 @pytest.fixture()
-def mock_get_active_insync_subscriptions():
-    with patch("gso.schedules.validate_subscriptions.get_active_insync_subscriptions") as mock:
+def mock_get_active_subscriptions():
+    with patch("gso.schedules.validate_subscriptions.get_active_subscriptions") as mock:
         yield mock
 
 
@@ -82,24 +82,24 @@ def test_scheduled_task_still_works():
     assert result == "task result"
 
 
-def test_no_subscriptions(mock_get_active_insync_subscriptions, mock_logger, validate_subscriptions):
-    mock_get_active_insync_subscriptions.return_value = []
+def test_no_subscriptions(mock_get_active_subscriptions, mock_logger, validate_subscriptions):
+    mock_get_active_subscriptions.return_value = []
     validate_subscriptions()
     mock_logger.info.assert_called_once_with("No subscriptions to validate")
 
 
 def test_subscriptions_without_system_target_workflow(
-    mock_get_active_insync_subscriptions,
+    mock_get_active_subscriptions,
     mock_logger,
     validate_subscriptions,
 ):
-    mock_get_active_insync_subscriptions.return_value = [MagicMock(product=MagicMock(workflows=[]))]
+    mock_get_active_subscriptions.return_value = [MagicMock(product=MagicMock(workflows=[]))]
     validate_subscriptions()
     mock_logger.warning.assert_called_once()
 
 
 def test_subscription_status_not_usable(
-    mock_get_active_insync_subscriptions,
+    mock_get_active_subscriptions,
     mock_get_execution_context,
     validate_subscriptions,
 ):
@@ -107,7 +107,7 @@ def test_subscription_status_not_usable(
     subscription_mock.product.workflows = [MagicMock(target=Target.SYSTEM, name="workflow_name")]
     subscription_mock.status = "Not Usable Status"
 
-    mock_get_active_insync_subscriptions.return_value = [subscription_mock]
+    mock_get_active_subscriptions.return_value = [subscription_mock]
     validate_subscriptions()
 
     validate_func = mock_get_execution_context()["validate"]
@@ -115,7 +115,7 @@ def test_subscription_status_not_usable(
 
 
 def test_valid_subscriptions_for_validation(
-    mock_get_active_insync_subscriptions,
+    mock_get_active_subscriptions,
     mock_get_execution_context,
     validate_subscriptions,
 ):
@@ -123,7 +123,7 @@ def test_valid_subscriptions_for_validation(
     mocked_workflow = MagicMock(target=Target.SYSTEM, name="workflow_name")
     subscription_mock.product.workflows = [mocked_workflow]
     subscription_mock.status = "active"
-    mock_get_active_insync_subscriptions.return_value = [subscription_mock]
+    mock_get_active_subscriptions.return_value = [subscription_mock]
     validate_subscriptions()
     validate_func = mock_get_execution_context()["validate"]
     validate_func.assert_called_once_with(