diff --git a/common_py/src/videoag_common/media_process/ffmpeg_target.py b/common_py/src/videoag_common/media_process/ffmpeg_target.py
index fa38a52319e2bcd18377e6f30ae709902806f1dc..74d4b9b299535dc988e49a7224dafad0ba85b811 100644
--- a/common_py/src/videoag_common/media_process/ffmpeg_target.py
+++ b/common_py/src/videoag_common/media_process/ffmpeg_target.py
@@ -168,18 +168,17 @@ class FFmpegFilterGraphTargetProducer(TargetProducer):
                                      process_sha256: str,
                                      input_media_by_id: dict[str, "MediumMetadata"]
                                      ) -> tuple[str, _I] or None:
-        sha256 = bytearray(32)
+        hash_concat = ""
         intermediate = []
         for node in self._ordered_node_list:
             node_res = node.calculate_current_input_hash(session, lecture, process_sha256, input_media_by_id)
             if node_res is None:
                 return None
             node_sha256, node_intermediate = node_res
-            sha256 = bytearray(x ^ y for x, y in zip(sha256, bytearray.fromhex(node_sha256)))
+            hash_concat += node_sha256
             intermediate.append(node_intermediate)
         
-        assert len(sha256) == 32
-        return sha256.hex(), intermediate
+        return hash_sha256(hash_concat), intermediate
     
     def create_job_data_to_produce_files(self,
                                          session: SessionDb,
diff --git a/common_py/src/videoag_common/media_process/jnode/video_slide_jnode.py b/common_py/src/videoag_common/media_process/jnode/video_slide_jnode.py
index a78a50d192ddcf804be14012ca0ced8e2d78f616..41bbb185e46d792e799063ccd6612226725fdf52 100644
--- a/common_py/src/videoag_common/media_process/jnode/video_slide_jnode.py
+++ b/common_py/src/videoag_common/media_process/jnode/video_slide_jnode.py
@@ -9,7 +9,7 @@ from videoag_common.ffmpeg import *
 
 from ..ffmpeg_target import JGraphContext
 from .basic_jnodes import EqualInOutNodeJGraphNode
-from ..target import _I
+from ..error import MediaProcessException
 
 if TYPE_CHECKING:
     from videoag_common.objects import Lecture, MediumMetadata, MediumFile
@@ -95,6 +95,16 @@ class TextVideoSlideRow(VideoSlideRow):
                 raise ValueError(f"Unknown variable '{var_name}'")
             return lecture_vars[var_name]
         return self._text_replace_vars(_var_replace)
+    
+    def get_used_lecture_variables(self) -> set[str]:
+        used_vars = set()
+        
+        def _var_counter_replace(var_name: str):
+            used_vars.add(var_name)
+            return ""
+        
+        self._text_replace_vars(_var_counter_replace)
+        return used_vars
 
 
 class SpacerVideoSlideRow(VideoSlideRow):
@@ -115,6 +125,13 @@ class VideoSlide(JsonDataClass):
     def __post_init__(self):
         if sum(row.height for row in self.rows) > 100:
             raise JsonSerializableInitException("Sum of row 'height' values must be <= 100")
+    
+    def get_used_lecture_variables(self) -> set[str]:
+        used_vars = set()
+        for row in self.rows:
+            if isinstance(row, TextVideoSlideRow):
+                used_vars.update(row.get_used_lecture_variables())
+        return used_vars
 
 
 class VideoTimePosition(JsonSerializableEnum):
@@ -139,7 +156,15 @@ class VideoSlideJGraphNode(EqualInOutNodeJGraphNode):
                                      input_media_by_id: dict[str, "MediumMetadata"]
                                      ) -> tuple[str, dict[str, str]] or None:
         lecture_text_vars = get_lecture_text_vars(lecture)
-        return hash_json_sha256(lecture_text_vars), lecture_text_vars
+        used_lecture_text_vars = {}
+        for var_name in self.slide.get_used_lecture_variables():
+            val = lecture_text_vars[var_name]
+            if val is None or val == "":
+                raise MediaProcessException(f"Lecture text variable '{var_name}' is null or empty. Set a value or don't"
+                                            f" use the variable in the video slide")
+            used_lecture_text_vars[var_name] = val
+        
+        return hash_json_sha256(used_lecture_text_vars), used_lecture_text_vars
     
     def create_job_data_to_build_filter_graph(self,
                                               session: SessionDb,