diff --git a/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/CheckSteps.py b/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/CheckSteps.py
index 912d566f1dc507673c3d05da6161ffd550eeeb7f..460afdbca8cae9b36fe2691ef2f4ec9ef96d9f6f 100644
--- a/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/CheckSteps.py
+++ b/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/CheckSteps.py
@@ -6,7 +6,6 @@
 Definitions of post-exec check steps in Trigger ART tests
 '''
 
-import sys
 import os
 import re
 import subprocess
@@ -29,9 +28,7 @@ class RefComparisonStep(Step):
 
     def configure(self, test):
         if self.reference and self.ref_test_name:
-            self.log.error('%s: Misconfiguration, both options "reference" and "ref_test_name" used. Use at most one of them.')
-            self.report_result(1, 'TestConfig')
-            sys.exit(1)
+            self.misconfig_abort('Both options "reference" and "ref_test_name" used. Use at most one of them.')
 
         if not self.ref_test_name:
             self.ref_test_name = test.name
@@ -56,10 +53,7 @@ class RefComparisonStep(Step):
                 return super(RefComparisonStep, self).configure(test)
 
         if self.input_file is None:
-            self.log.error('Cannot configure %s because input_file not specified',
-                           self.name)
-            self.report_result(1, 'TestConfig')
-            sys.exit(1)
+            self.misconfig_abort('input_file not specified')
 
         branch = os.environ.get('AtlasBuildBranch')  # Available after asetup
         if branch is None:
@@ -132,10 +126,9 @@ class LogMergeStep(Step):
                 self.log_files.append(step.name)
         # Protect against infinite loop
         if self.merged_name in self.log_files:
-            self.log.error('%s output log name %s is same as one of the input log names.'\
-                           ' This will lead to infinite loop, aborting.', self.name, self.merged_name)
-            self.report_result(1, 'TestConfig')
-            sys.exit(1)
+            self.misconfig_abort(
+                'output log name %s is same as one of the input log names.'
+                ' This will lead to infinite loop, aborting.', self.merged_name)
         super(LogMergeStep, self).configure(test)
 
     def process_extra_regex(self):
diff --git a/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/ExecStep.py b/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/ExecStep.py
index d935df5bd430ff699130211c297762c9639773d6..b0edece9041c32f4a4dd4e7c322b9070f1bb5e73 100644
--- a/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/ExecStep.py
+++ b/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/ExecStep.py
@@ -6,7 +6,6 @@
 Definitions of exec steps (main job) in Trigger ART tests
 '''
 
-import sys
 import os
 
 from TrigValTools.TrigValSteering.Step import Step
@@ -71,10 +70,7 @@ class ExecStep(Step):
         self.log.debug('Configuring type for step %s', self.name)
         # Check if type or executable is specified
         if self.type is None and self.executable is None:
-            self.log.error('Cannot configure a step without specified type '
-                           'or executable')
-            self.report_result(1, 'TestConfig')
-            sys.exit(1)
+            self.misconfig_abort('Cannot configure a step without specified type or executable')
 
         # Configure executable from type
         known_types = ['athena', 'athenaHLT', 'Reco_tf', 'Trig_reco_tf']
@@ -87,9 +83,7 @@ class ExecStep(Step):
         elif self.type == 'other' or self.type is None:
             self.type = 'other'
         else:
-            self.log.error('Cannot determine type of step %s', self.name)
-            self.report_result(1, 'TestConfig')
-            sys.exit(1)
+            self.misconfig_abort('Cannot determine type of this step')
 
         # Ensure no log duplication for transforms
         if self.executable.endswith('_tf.py'):
@@ -100,12 +94,9 @@ class ExecStep(Step):
     def configure_input(self):
         self.log.debug('Configuring input for step %s', self.name)
         if self.input is None:
-            self.log.error(
-                'Input not provided for step %s. To configure '
-                'a step without input, use an empty string',
-                self.name)
-            self.report_result(1, 'TestConfig')
-            sys.exit(1)
+            self.misconfig_abort(
+                'Input not provided for this step. To configure'
+                'a step without input, use an empty string')
 
         # Step with no input
         if len(self.input) == 0:
@@ -115,18 +106,14 @@ class ExecStep(Step):
         if is_input_defined(self.input):
             self.input_object = get_input(self.input)
             if self.input_object is None:
-                self.log.error('Failed to load input with keyword %s', self.input)
-                self.report_result(1, 'TestConfig')
-                sys.exit(1)
+                self.misconfig_abort('Failed to load input with keyword %s', self.input)
             return
 
         # Try to interpret explicit paths
         input_paths = self.input.split(',')
         for path in input_paths:
             if not os.path.isfile(path):
-                self.log.error('The provided input does not exist: %s', self.input)
-                self.report_result(1, 'TestConfig')
-                sys.exit(1)
+                self.misconfig_abort('The provided input does not exist: %s', self.input)
         self.log.debug('Using explicit input: %s', self.input)
 
     def configure_job_options(self):
@@ -143,21 +130,14 @@ class ExecStep(Step):
             if self.job_options is None:
                 return
             else:
-                self.log.error('Transform %s does not accept job options',
-                               self.type)
-                self.report_result(1, 'TestConfig')
-                sys.exit(1)
+                self.misconfig_abort('Transform %s does not accept job options', self.type)
         elif self.job_options is None or len(self.job_options) == 0:
-            self.log.error('Job options not provided for step %s', self.name)
-            self.report_result(1, 'TestConfig')
-            sys.exit(1)
+            self.misconfig_abort('Job options not provided for this step')
         # Check if job options exist
         if check_job_options(self.job_options):
             self.log.debug('Job options file exists: %s', self.job_options)
         else:
-            self.log.error('Failed to find job options file %s for step %s', self.job_options, self.name)
-            self.report_result(1, 'TestConfig')
-            sys.exit(1)
+            self.misconfig_abort('Failed to find job options file %s', self.job_options)
 
     def configure_args(self, test):
         self.log.debug('Configuring args for step %s', self.name)
@@ -256,12 +236,10 @@ class ExecStep(Step):
                 self.args += ' --file={}'.format(input_str)
             elif self.type.endswith('_tf'):
                 if self.input_object is None:
-                    self.log.error(
+                    self.misconfig_abort(
                         'Cannot build inputXYZFile string for transform '
                         ' from explicit input path. Use input=\'\' and '
                         'specify the input explicitly in args')
-                    self.report_result(1, 'TestConfig')
-                    sys.exit(1)
                 if self.type == 'Trig_reco_tf' and '--prodSysBSRDO True' in self.args:
                     self.args += ' --inputBS_RDOFile={}'.format(input_str)
                 else:
diff --git a/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/Step.py b/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/Step.py
index e606b339c97b2d836718b784114348be0eae3e69..d8348957484ac6c7f5c69325d78ba842e9bab711 100644
--- a/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/Step.py
+++ b/Trigger/TrigValidation/TrigValTools/python/TrigValSteering/Step.py
@@ -60,9 +60,7 @@ class Step(object):
             if self.result is not None:
                 result = self.result
             else:
-                self.log.error('report_result was called but result is None')
-                self.report_result(1, 'TestConfig')
-                sys.exit(1)
+                self.misconfig_abort('report_result was called but result is None')
 
         if name is None:
             if self.name is not None:
@@ -72,6 +70,15 @@ class Step(object):
 
         art_result(result, name)
 
+    def misconfig_abort(self, error_msg, *args, **kwargs):
+        '''
+        Print an error message (arguments passed to logging.error),
+        report non-zero art-result and exit the process with non-zero code
+        '''
+        self.log.error('Misconfiguration in %s: '+error_msg, self.name, *args, **kwargs)
+        self.report_result(1, 'TestConfig')
+        sys.exit(1)
+
     def __trace_and_kill(self, pid, signal, backtrace_list):
         '''
         Produce a backtrace for a process and its children, then call