diff --git a/polkadot/node/core/approval-voting/src/lib.rs b/polkadot/node/core/approval-voting/src/lib.rs
index 94f7fcaf9411e3122c15ba4b706d4333afb5dea2..ef01727b7eb681a180608144cee26d91d0bbda44 100644
--- a/polkadot/node/core/approval-voting/src/lib.rs
+++ b/polkadot/node/core/approval-voting/src/lib.rs
@@ -54,7 +54,7 @@ use polkadot_node_subsystem_util::{
 };
 use polkadot_primitives::{
 	ApprovalVote, BlockNumber, CandidateHash, CandidateIndex, CandidateReceipt, DisputeStatement,
-	ExecutorParams, GroupIndex, Hash, PvfExecTimeoutKind, SessionIndex, SessionInfo,
+	ExecutorParams, GroupIndex, Hash, PvfExecKind, SessionIndex, SessionInfo,
 	ValidDisputeStatementKind, ValidatorId, ValidatorIndex, ValidatorPair, ValidatorSignature,
 };
 use sc_keystore::LocalKeystore;
@@ -2867,7 +2867,7 @@ async fn launch_approval<Context>(
 				candidate_receipt: candidate.clone(),
 				pov: available_data.pov,
 				executor_params,
-				exec_timeout_kind: PvfExecTimeoutKind::Approval,
+				exec_kind: PvfExecKind::Approval,
 				response_sender: val_tx,
 			})
 			.await;
diff --git a/polkadot/node/core/approval-voting/src/tests.rs b/polkadot/node/core/approval-voting/src/tests.rs
index 0c0dcfde9b6660e181646e6689e1ab26735554d9..11bcba9c388277fa47a305106f30aac253913045 100644
--- a/polkadot/node/core/approval-voting/src/tests.rs
+++ b/polkadot/node/core/approval-voting/src/tests.rs
@@ -2705,10 +2705,10 @@ async fn handle_double_assignment_import(
 	assert_matches!(
 		overseer_recv(virtual_overseer).await,
 		AllMessages::CandidateValidation(CandidateValidationMessage::ValidateFromExhaustive {
-			exec_timeout_kind,
+			exec_kind,
 			response_sender,
 			..
-		}) if exec_timeout_kind == PvfExecTimeoutKind::Approval => {
+		}) if exec_kind == PvfExecKind::Approval => {
 			response_sender.send(Ok(ValidationResult::Valid(Default::default(), Default::default())))
 				.unwrap();
 		}
diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs
index a91eefe5e04f8790896dfe953481ab81597c1d09..434051f1b00f490504f1384cda6ecb03f4fc7703 100644
--- a/polkadot/node/core/backing/src/lib.rs
+++ b/polkadot/node/core/backing/src/lib.rs
@@ -106,7 +106,7 @@ use polkadot_node_subsystem_util::{
 use polkadot_primitives::{
 	BackedCandidate, CandidateCommitments, CandidateHash, CandidateReceipt,
 	CommittedCandidateReceipt, CoreIndex, CoreState, ExecutorParams, Hash, Id as ParaId,
-	PersistedValidationData, PvfExecTimeoutKind, SigningContext, ValidationCode, ValidatorId,
+	PersistedValidationData, PvfExecKind, SigningContext, ValidationCode, ValidatorId,
 	ValidatorIndex, ValidatorSignature, ValidityAttestation,
 };
 use sp_keystore::KeystorePtr;
@@ -566,7 +566,7 @@ async fn request_candidate_validation(
 			candidate_receipt,
 			pov,
 			executor_params,
-			exec_timeout_kind: PvfExecTimeoutKind::Backing,
+			exec_kind: PvfExecKind::Backing,
 			response_sender: tx,
 		})
 		.await;
diff --git a/polkadot/node/core/backing/src/tests/mod.rs b/polkadot/node/core/backing/src/tests/mod.rs
index caa85c12989c395fbdcd4af5253cbb15275debe9..c12be72556e36dfe62af71056bea379aa934de35 100644
--- a/polkadot/node/core/backing/src/tests/mod.rs
+++ b/polkadot/node/core/backing/src/tests/mod.rs
@@ -33,7 +33,7 @@ use polkadot_node_subsystem::{
 };
 use polkadot_node_subsystem_test_helpers as test_helpers;
 use polkadot_primitives::{
-	CandidateDescriptor, GroupRotationInfo, HeadData, PersistedValidationData, PvfExecTimeoutKind,
+	CandidateDescriptor, GroupRotationInfo, HeadData, PersistedValidationData, PvfExecKind,
 	ScheduledCore, SessionIndex, LEGACY_MIN_BACKING_VOTES,
 };
 use sp_application_crypto::AppCrypto;
@@ -344,14 +344,14 @@ async fn assert_validate_from_exhaustive(
 				validation_data,
 				validation_code,
 				candidate_receipt,
-				exec_timeout_kind,
+				exec_kind,
 				response_sender,
 				..
 			},
 		) if validation_data == *assert_pvd &&
 			validation_code == *assert_validation_code &&
 			*pov == *assert_pov && &candidate_receipt.descriptor == assert_candidate.descriptor() &&
-			exec_timeout_kind == PvfExecTimeoutKind::Backing &&
+			exec_kind == PvfExecKind::Backing &&
 			candidate_receipt.commitments_hash == assert_candidate.commitments.hash() =>
 		{
 			response_sender.send(Ok(ValidationResult::Valid(
@@ -550,14 +550,14 @@ fn backing_works() {
 					validation_code,
 					candidate_receipt,
 					pov,
-					exec_timeout_kind,
+					exec_kind,
 					response_sender,
 					..
 				},
 			) if validation_data == pvd_ab &&
 				validation_code == validation_code_ab &&
 				*pov == pov_ab && &candidate_receipt.descriptor == candidate_a.descriptor() &&
-				exec_timeout_kind == PvfExecTimeoutKind::Backing &&
+				exec_kind == PvfExecKind::Backing &&
 				candidate_receipt.commitments_hash == candidate_a_commitments_hash =>
 			{
 				response_sender.send(Ok(
@@ -729,14 +729,14 @@ fn backing_works_while_validation_ongoing() {
 					validation_code,
 					candidate_receipt,
 					pov,
-					exec_timeout_kind,
+					exec_kind,
 					response_sender,
 					..
 				},
 			) if validation_data == pvd_abc &&
 				validation_code == validation_code_abc &&
 				*pov == pov_abc && &candidate_receipt.descriptor == candidate_a.descriptor() &&
-				exec_timeout_kind == PvfExecTimeoutKind::Backing &&
+				exec_kind == PvfExecKind::Backing &&
 				candidate_a_commitments_hash == candidate_receipt.commitments_hash =>
 			{
 				// we never validate the candidate. our local node
@@ -890,14 +890,14 @@ fn backing_misbehavior_works() {
 					validation_code,
 					candidate_receipt,
 					pov,
-					exec_timeout_kind,
+					exec_kind,
 					response_sender,
 					..
 				},
 			) if validation_data == pvd_a &&
 				validation_code == validation_code_a &&
 				*pov == pov_a && &candidate_receipt.descriptor == candidate_a.descriptor() &&
-				exec_timeout_kind == PvfExecTimeoutKind::Backing &&
+				exec_kind == PvfExecKind::Backing &&
 				candidate_a_commitments_hash == candidate_receipt.commitments_hash =>
 			{
 				response_sender.send(Ok(
@@ -1057,14 +1057,14 @@ fn backing_dont_second_invalid() {
 					validation_code,
 					candidate_receipt,
 					pov,
-					exec_timeout_kind,
+					exec_kind,
 					response_sender,
 					..
 				},
 			) if validation_data == pvd_a &&
 				validation_code == validation_code_a &&
 				*pov == pov_block_a && &candidate_receipt.descriptor == candidate_a.descriptor() &&
-				exec_timeout_kind == PvfExecTimeoutKind::Backing &&
+				exec_kind == PvfExecKind::Backing &&
 				candidate_a.commitments.hash() == candidate_receipt.commitments_hash =>
 			{
 				response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap();
@@ -1097,14 +1097,14 @@ fn backing_dont_second_invalid() {
 					validation_code,
 					candidate_receipt,
 					pov,
-					exec_timeout_kind,
+					exec_kind,
 					response_sender,
 					..
 				},
 			) if validation_data == pvd_b &&
 				validation_code == validation_code_b &&
 				*pov == pov_block_b && &candidate_receipt.descriptor == candidate_b.descriptor() &&
-				exec_timeout_kind == PvfExecTimeoutKind::Backing &&
+				exec_kind == PvfExecKind::Backing &&
 				candidate_b.commitments.hash() == candidate_receipt.commitments_hash =>
 			{
 				response_sender.send(Ok(
@@ -1224,14 +1224,14 @@ fn backing_second_after_first_fails_works() {
 					validation_code,
 					candidate_receipt,
 					pov,
-					exec_timeout_kind,
+					exec_kind,
 					response_sender,
 					..
 				},
 			) if validation_data == pvd_a &&
 				validation_code == validation_code_a &&
 				*pov == pov_a && &candidate_receipt.descriptor == candidate.descriptor() &&
-				exec_timeout_kind == PvfExecTimeoutKind::Backing &&
+				exec_kind == PvfExecKind::Backing &&
 				candidate.commitments.hash() == candidate_receipt.commitments_hash =>
 			{
 				response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::BadReturn))).unwrap();
@@ -1368,14 +1368,14 @@ fn backing_works_after_failed_validation() {
 					validation_code,
 					candidate_receipt,
 					pov,
-					exec_timeout_kind,
+					exec_kind,
 					response_sender,
 					..
 				},
 			) if validation_data == pvd_a &&
 				validation_code == validation_code_a &&
 				*pov == pov_a && &candidate_receipt.descriptor == candidate.descriptor() &&
-				exec_timeout_kind == PvfExecTimeoutKind::Backing &&
+				exec_kind == PvfExecKind::Backing &&
 				candidate.commitments.hash() == candidate_receipt.commitments_hash =>
 			{
 				response_sender.send(Err(ValidationFailed("Internal test error".into()))).unwrap();
@@ -1634,13 +1634,13 @@ fn retry_works() {
 					validation_code,
 					candidate_receipt,
 					pov,
-					exec_timeout_kind,
+					exec_kind,
 					..
 				},
 			) if validation_data == pvd_a &&
 				validation_code == validation_code_a &&
 				*pov == pov_a && &candidate_receipt.descriptor == candidate.descriptor() &&
-				exec_timeout_kind == PvfExecTimeoutKind::Backing &&
+				exec_kind == PvfExecKind::Backing &&
 				candidate.commitments.hash() == candidate_receipt.commitments_hash
 		);
 		virtual_overseer
diff --git a/polkadot/node/core/backing/src/tests/prospective_parachains.rs b/polkadot/node/core/backing/src/tests/prospective_parachains.rs
index fc4bd7d98e7d229bc3738e60273a484349c2ee79..e7c29e11bb4702a1af46d414af0620af39910e1f 100644
--- a/polkadot/node/core/backing/src/tests/prospective_parachains.rs
+++ b/polkadot/node/core/backing/src/tests/prospective_parachains.rs
@@ -232,14 +232,14 @@ async fn assert_validate_seconded_candidate(
 			validation_code,
 			candidate_receipt,
 			pov,
-			exec_timeout_kind,
+			exec_kind,
 			response_sender,
 			..
 		}) if &validation_data == assert_pvd &&
 			&validation_code == assert_validation_code &&
 			&*pov == assert_pov &&
 			&candidate_receipt.descriptor == candidate.descriptor() &&
-			exec_timeout_kind == PvfExecTimeoutKind::Backing &&
+			exec_kind == PvfExecKind::Backing &&
 			candidate.commitments.hash() == candidate_receipt.commitments_hash =>
 		{
 			response_sender.send(Ok(ValidationResult::Valid(
diff --git a/polkadot/node/core/candidate-validation/src/lib.rs b/polkadot/node/core/candidate-validation/src/lib.rs
index 96ab07d3b8241f46d205277707791ada7fb5d305..f5d17af6c689ad8e56d608f574731efaf7673731 100644
--- a/polkadot/node/core/candidate-validation/src/lib.rs
+++ b/polkadot/node/core/candidate-validation/src/lib.rs
@@ -49,8 +49,8 @@ use polkadot_primitives::{
 		DEFAULT_LENIENT_PREPARATION_TIMEOUT, DEFAULT_PRECHECK_PREPARATION_TIMEOUT,
 	},
 	CandidateCommitments, CandidateDescriptor, CandidateReceipt, ExecutorParams, Hash,
-	OccupiedCoreAssumption, PersistedValidationData, PvfExecTimeoutKind, PvfPrepTimeoutKind,
-	ValidationCode, ValidationCodeHash,
+	OccupiedCoreAssumption, PersistedValidationData, PvfExecKind, PvfPrepKind, ValidationCode,
+	ValidationCodeHash,
 };
 
 use parity_scale_codec::Encode;
@@ -73,12 +73,6 @@ mod tests;
 
 const LOG_TARGET: &'static str = "parachain::candidate-validation";
 
-/// The amount of time to wait before retrying after a retry-able backing validation error. We use a
-/// lower value for the backing case, to fit within the lower backing timeout.
-#[cfg(not(test))]
-const PVF_BACKING_EXECUTION_RETRY_DELAY: Duration = Duration::from_millis(500);
-#[cfg(test)]
-const PVF_BACKING_EXECUTION_RETRY_DELAY: Duration = Duration::from_millis(200);
 /// The amount of time to wait before retrying after a retry-able approval validation error. We use
 /// a higher value for the approval case since we have more time, and if we wait longer it is more
 /// likely that transient conditions will resolve.
@@ -163,7 +157,7 @@ async fn run<Context>(
 					candidate_receipt,
 					pov,
 					executor_params,
-					exec_timeout_kind,
+					exec_kind,
 					response_sender,
 					..
 				} => {
@@ -180,7 +174,7 @@ async fn run<Context>(
 								candidate_receipt,
 								pov,
 								executor_params,
-								exec_timeout_kind,
+								exec_kind,
 								&metrics,
 							)
 							.await;
@@ -198,7 +192,7 @@ async fn run<Context>(
 					candidate_receipt,
 					pov,
 					executor_params,
-					exec_timeout_kind,
+					exec_kind,
 					response_sender,
 					..
 				} => {
@@ -215,7 +209,7 @@ async fn run<Context>(
 								candidate_receipt,
 								pov,
 								executor_params,
-								exec_timeout_kind,
+								exec_kind,
 								&metrics,
 							)
 							.await;
@@ -357,7 +351,7 @@ where
 			return PreCheckOutcome::Invalid
 		};
 
-	let timeout = pvf_prep_timeout(&executor_params, PvfPrepTimeoutKind::Precheck);
+	let timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Precheck);
 
 	let pvf = match sp_maybe_compressed_blob::decompress(
 		&validation_code.0,
@@ -501,7 +495,7 @@ async fn validate_from_chain_state<Sender>(
 	candidate_receipt: CandidateReceipt,
 	pov: Arc<PoV>,
 	executor_params: ExecutorParams,
-	exec_timeout_kind: PvfExecTimeoutKind,
+	exec_kind: PvfExecKind,
 	metrics: &Metrics,
 ) -> Result<ValidationResult, ValidationFailed>
 where
@@ -521,7 +515,7 @@ where
 		candidate_receipt.clone(),
 		pov,
 		executor_params,
-		exec_timeout_kind,
+		exec_kind,
 		metrics,
 	)
 	.await;
@@ -557,7 +551,7 @@ async fn validate_candidate_exhaustive(
 	candidate_receipt: CandidateReceipt,
 	pov: Arc<PoV>,
 	executor_params: ExecutorParams,
-	exec_timeout_kind: PvfExecTimeoutKind,
+	exec_kind: PvfExecKind,
 	metrics: &Metrics,
 ) -> Result<ValidationResult, ValidationFailed> {
 	let _timer = metrics.time_validate_candidate_exhaustive();
@@ -616,15 +610,32 @@ async fn validate_candidate_exhaustive(
 		relay_parent_storage_root: persisted_validation_data.relay_parent_storage_root,
 	};
 
-	let result = validation_backend
-		.validate_candidate_with_retry(
-			raw_validation_code.to_vec(),
-			pvf_exec_timeout(&executor_params, exec_timeout_kind),
-			exec_timeout_kind,
-			params,
-			executor_params,
-		)
-		.await;
+	let result = match exec_kind {
+		// Retry is disabled to reduce the chance of nondeterministic blocks getting backed and
+		// honest backers getting slashed.
+		PvfExecKind::Backing => {
+			let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare);
+			let exec_timeout = pvf_exec_timeout(&executor_params, exec_kind);
+			let pvf = PvfPrepData::from_code(
+				raw_validation_code.to_vec(),
+				executor_params,
+				prep_timeout,
+				PrepareJobKind::Compilation,
+			);
+
+			validation_backend.validate_candidate(pvf, exec_timeout, params.encode()).await
+		},
+		PvfExecKind::Approval =>
+			validation_backend
+				.validate_candidate_with_retry(
+					raw_validation_code.to_vec(),
+					pvf_exec_timeout(&executor_params, exec_kind),
+					params,
+					executor_params,
+					PVF_APPROVAL_EXECUTION_RETRY_DELAY,
+				)
+				.await,
+	};
 
 	if let Err(ref error) = result {
 		gum::info!(target: LOG_TARGET, ?para_id, ?error, "Failed to validate candidate");
@@ -709,8 +720,8 @@ trait ValidationBackend {
 		encoded_params: Vec<u8>,
 	) -> Result<WasmValidationResult, ValidationError>;
 
-	/// Tries executing a PVF. Will retry once if an error is encountered that may have been
-	/// transient.
+	/// Tries executing a PVF for the approval subsystem. Will retry once if an error is encountered
+	/// that may have been transient.
 	///
 	/// NOTE: Should retry only on errors that are a result of execution itself, and not of
 	/// preparation.
@@ -718,11 +729,11 @@ trait ValidationBackend {
 		&mut self,
 		raw_validation_code: Vec<u8>,
 		exec_timeout: Duration,
-		exec_timeout_kind: PvfExecTimeoutKind,
 		params: ValidationParams,
 		executor_params: ExecutorParams,
+		retry_delay: Duration,
 	) -> Result<WasmValidationResult, ValidationError> {
-		let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepTimeoutKind::Lenient);
+		let prep_timeout = pvf_prep_timeout(&executor_params, PvfPrepKind::Prepare);
 		// Construct the PVF a single time, since it is an expensive operation. Cloning it is cheap.
 		let pvf = PvfPrepData::from_code(
 			raw_validation_code,
@@ -740,11 +751,6 @@ trait ValidationBackend {
 			return validation_result
 		}
 
-		let retry_delay = match exec_timeout_kind {
-			PvfExecTimeoutKind::Backing => PVF_BACKING_EXECUTION_RETRY_DELAY,
-			PvfExecTimeoutKind::Approval => PVF_APPROVAL_EXECUTION_RETRY_DELAY,
-		};
-
 		// Allow limited retries for each kind of error.
 		let mut num_death_retries_left = 1;
 		let mut num_job_error_retries_left = 1;
@@ -867,22 +873,41 @@ fn perform_basic_checks(
 	Ok(())
 }
 
-fn pvf_prep_timeout(executor_params: &ExecutorParams, kind: PvfPrepTimeoutKind) -> Duration {
+/// To determine the amount of timeout time for the pvf execution.
+///
+/// Precheck
+///	The time period after which the preparation worker is considered
+/// unresponsive and will be killed.
+///
+/// Prepare
+///The time period after which the preparation worker is considered
+/// unresponsive and will be killed.
+fn pvf_prep_timeout(executor_params: &ExecutorParams, kind: PvfPrepKind) -> Duration {
 	if let Some(timeout) = executor_params.pvf_prep_timeout(kind) {
 		return timeout
 	}
 	match kind {
-		PvfPrepTimeoutKind::Precheck => DEFAULT_PRECHECK_PREPARATION_TIMEOUT,
-		PvfPrepTimeoutKind::Lenient => DEFAULT_LENIENT_PREPARATION_TIMEOUT,
+		PvfPrepKind::Precheck => DEFAULT_PRECHECK_PREPARATION_TIMEOUT,
+		PvfPrepKind::Prepare => DEFAULT_LENIENT_PREPARATION_TIMEOUT,
 	}
 }
 
-fn pvf_exec_timeout(executor_params: &ExecutorParams, kind: PvfExecTimeoutKind) -> Duration {
+/// To determine the amount of timeout time for the pvf execution.
+///
+/// Backing subsystem
+/// The amount of time to spend on execution during backing.
+///
+/// Approval subsystem
+/// The amount of time to spend on execution during approval or disputes.
+/// This should be much longer than the backing execution timeout to ensure that in the
+/// absence of extremely large disparities between hardware, blocks that pass backing are
+/// considered executable by approval checkers or dispute participants.
+fn pvf_exec_timeout(executor_params: &ExecutorParams, kind: PvfExecKind) -> Duration {
 	if let Some(timeout) = executor_params.pvf_exec_timeout(kind) {
 		return timeout
 	}
 	match kind {
-		PvfExecTimeoutKind::Backing => DEFAULT_BACKING_EXECUTION_TIMEOUT,
-		PvfExecTimeoutKind::Approval => DEFAULT_APPROVAL_EXECUTION_TIMEOUT,
+		PvfExecKind::Backing => DEFAULT_BACKING_EXECUTION_TIMEOUT,
+		PvfExecKind::Approval => DEFAULT_APPROVAL_EXECUTION_TIMEOUT,
 	}
 }
diff --git a/polkadot/node/core/candidate-validation/src/tests.rs b/polkadot/node/core/candidate-validation/src/tests.rs
index 801c581938a664241e99a28c85cb4a819959e9bf..5e2585d68735e4b72e778eefb4907cd8171101c7 100644
--- a/polkadot/node/core/candidate-validation/src/tests.rs
+++ b/polkadot/node/core/candidate-validation/src/tests.rs
@@ -436,7 +436,7 @@ fn candidate_validation_ok_is_ok() {
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		PvfExecKind::Backing,
 		&Default::default(),
 	))
 	.unwrap();
@@ -488,7 +488,7 @@ fn candidate_validation_bad_return_is_invalid() {
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		PvfExecKind::Backing,
 		&Default::default(),
 	))
 	.unwrap();
@@ -496,23 +496,20 @@ fn candidate_validation_bad_return_is_invalid() {
 	assert_matches!(v, ValidationResult::Invalid(InvalidCandidate::Timeout));
 }
 
-// Test that we vote valid if we get `AmbiguousWorkerDeath`, retry, and then succeed.
-#[test]
-fn candidate_validation_one_ambiguous_error_is_valid() {
-	let validation_data = PersistedValidationData { max_pov_size: 1024, ..Default::default() };
-
-	let pov = PoV { block_data: BlockData(vec![1; 32]) };
-	let head_data = HeadData(vec![1, 1, 1]);
-	let validation_code = ValidationCode(vec![2; 16]);
-
+fn perform_basic_checks_on_valid_candidate(
+	pov: &PoV,
+	validation_code: &ValidationCode,
+	validation_data: &PersistedValidationData,
+	head_data_hash: Hash,
+) -> CandidateDescriptor {
 	let descriptor = make_valid_candidate_descriptor(
 		ParaId::from(1_u32),
 		dummy_hash(),
 		validation_data.hash(),
 		pov.hash(),
 		validation_code.hash(),
-		head_data.hash(),
-		dummy_hash(),
+		head_data_hash,
+		head_data_hash,
 		Sr25519Keyring::Alice,
 	);
 
@@ -523,6 +520,24 @@ fn candidate_validation_one_ambiguous_error_is_valid() {
 		&validation_code.hash(),
 	);
 	assert!(check.is_ok());
+	descriptor
+}
+
+// Test that we vote valid if we get `AmbiguousWorkerDeath`, retry, and then succeed.
+#[test]
+fn candidate_validation_one_ambiguous_error_is_valid() {
+	let validation_data = PersistedValidationData { max_pov_size: 1024, ..Default::default() };
+
+	let pov = PoV { block_data: BlockData(vec![1; 32]) };
+	let head_data = HeadData(vec![1, 1, 1]);
+	let validation_code = ValidationCode(vec![2; 16]);
+
+	let descriptor = perform_basic_checks_on_valid_candidate(
+		&pov,
+		&validation_code,
+		&validation_data,
+		head_data.hash(),
+	);
 
 	let validation_result = WasmValidationResult {
 		head_data,
@@ -554,7 +569,7 @@ fn candidate_validation_one_ambiguous_error_is_valid() {
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		PvfExecKind::Approval,
 		&Default::default(),
 	))
 	.unwrap();
@@ -576,24 +591,12 @@ fn candidate_validation_multiple_ambiguous_errors_is_invalid() {
 	let pov = PoV { block_data: BlockData(vec![1; 32]) };
 	let validation_code = ValidationCode(vec![2; 16]);
 
-	let descriptor = make_valid_candidate_descriptor(
-		ParaId::from(1_u32),
-		dummy_hash(),
-		validation_data.hash(),
-		pov.hash(),
-		validation_code.hash(),
-		dummy_hash(),
-		dummy_hash(),
-		Sr25519Keyring::Alice,
-	);
-
-	let check = perform_basic_checks(
-		&descriptor,
-		validation_data.max_pov_size,
+	let descriptor = perform_basic_checks_on_valid_candidate(
 		&pov,
-		&validation_code.hash(),
+		&validation_code,
+		&validation_data,
+		dummy_hash(),
 	);
-	assert!(check.is_ok());
 
 	let candidate_receipt = CandidateReceipt { descriptor, commitments_hash: Hash::zero() };
 
@@ -607,7 +610,7 @@ fn candidate_validation_multiple_ambiguous_errors_is_invalid() {
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		PvfExecKind::Approval,
 		&Default::default(),
 	))
 	.unwrap();
@@ -615,58 +618,79 @@ fn candidate_validation_multiple_ambiguous_errors_is_invalid() {
 	assert_matches!(v, ValidationResult::Invalid(InvalidCandidate::ExecutionError(_)));
 }
 
-// Test that we retry on internal errors.
+// Test that we retry for approval on internal errors.
 #[test]
 fn candidate_validation_retry_internal_errors() {
-	let validation_data = PersistedValidationData { max_pov_size: 1024, ..Default::default() };
-
-	let pov = PoV { block_data: BlockData(vec![1; 32]) };
-	let validation_code = ValidationCode(vec![2; 16]);
-
-	let descriptor = make_valid_candidate_descriptor(
-		ParaId::from(1_u32),
-		dummy_hash(),
-		validation_data.hash(),
-		pov.hash(),
-		validation_code.hash(),
-		dummy_hash(),
-		dummy_hash(),
-		Sr25519Keyring::Alice,
-	);
-
-	let check = perform_basic_checks(
-		&descriptor,
-		validation_data.max_pov_size,
-		&pov,
-		&validation_code.hash(),
+	let v = candidate_validation_retry_on_error_helper(
+		PvfExecKind::Approval,
+		vec![
+			Err(InternalValidationError::HostCommunication("foo".into()).into()),
+			// Throw an AJD error, we should still retry again.
+			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::AmbiguousJobDeath(
+				"baz".into(),
+			))),
+			// Throw another internal error.
+			Err(InternalValidationError::HostCommunication("bar".into()).into()),
+		],
 	);
-	assert!(check.is_ok());
-
-	let candidate_receipt = CandidateReceipt { descriptor, commitments_hash: Hash::zero() };
+	assert_matches!(v, Err(ValidationFailed(s)) if s.contains("bar"));
+}
 
-	let v = executor::block_on(validate_candidate_exhaustive(
-		MockValidateCandidateBackend::with_hardcoded_result_list(vec![
+// Test that we don't retry for backing on internal errors.
+#[test]
+fn candidate_validation_dont_retry_internal_errors() {
+	let v = candidate_validation_retry_on_error_helper(
+		PvfExecKind::Backing,
+		vec![
 			Err(InternalValidationError::HostCommunication("foo".into()).into()),
 			// Throw an AWD error, we should still retry again.
 			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::AmbiguousWorkerDeath)),
 			// Throw another internal error.
 			Err(InternalValidationError::HostCommunication("bar".into()).into()),
-		]),
-		validation_data,
-		validation_code,
-		candidate_receipt,
-		Arc::new(pov),
-		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
-		&Default::default(),
-	));
+		],
+	);
 
-	assert_matches!(v, Err(ValidationFailed(s)) if s.contains("bar"));
+	assert_matches!(v, Err(ValidationFailed(s)) if s.contains("foo"));
 }
 
-// Test that we retry on panic errors.
+// Test that we retry for approval on panic errors.
 #[test]
 fn candidate_validation_retry_panic_errors() {
+	let v = candidate_validation_retry_on_error_helper(
+		PvfExecKind::Approval,
+		vec![
+			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::JobError("foo".into()))),
+			// Throw an AWD error, we should still retry again.
+			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::AmbiguousWorkerDeath)),
+			// Throw another panic error.
+			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::JobError("bar".into()))),
+		],
+	);
+
+	assert_matches!(v, Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(s))) if s == "bar".to_string());
+}
+
+// Test that we don't retry for backing on panic errors.
+#[test]
+fn candidate_validation_dont_retry_panic_errors() {
+	let v = candidate_validation_retry_on_error_helper(
+		PvfExecKind::Backing,
+		vec![
+			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::JobError("foo".into()))),
+			// Throw an AWD error, we should still retry again.
+			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::AmbiguousWorkerDeath)),
+			// Throw another panic error.
+			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::JobError("bar".into()))),
+		],
+	);
+
+	assert_matches!(v, Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(s))) if s == "foo".to_string());
+}
+
+fn candidate_validation_retry_on_error_helper(
+	exec_kind: PvfExecKind,
+	mock_errors: Vec<Result<WasmValidationResult, ValidationError>>,
+) -> Result<ValidationResult, ValidationFailed> {
 	let validation_data = PersistedValidationData { max_pov_size: 1024, ..Default::default() };
 
 	let pov = PoV { block_data: BlockData(vec![1; 32]) };
@@ -693,26 +717,16 @@ fn candidate_validation_retry_panic_errors() {
 
 	let candidate_receipt = CandidateReceipt { descriptor, commitments_hash: Hash::zero() };
 
-	let v = executor::block_on(validate_candidate_exhaustive(
-		MockValidateCandidateBackend::with_hardcoded_result_list(vec![
-			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::JobError("foo".into()))),
-			// Throw an AJD error, we should still retry again.
-			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::AmbiguousJobDeath(
-				"baz".into(),
-			))),
-			// Throw another panic error.
-			Err(ValidationError::InvalidCandidate(WasmInvalidCandidate::JobError("bar".into()))),
-		]),
+	return executor::block_on(validate_candidate_exhaustive(
+		MockValidateCandidateBackend::with_hardcoded_result_list(mock_errors),
 		validation_data,
 		validation_code,
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		exec_kind,
 		&Default::default(),
 	));
-
-	assert_matches!(v, Ok(ValidationResult::Invalid(InvalidCandidate::ExecutionError(s))) if s == "bar".to_string());
 }
 
 #[test]
@@ -752,7 +766,7 @@ fn candidate_validation_timeout_is_internal_error() {
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		PvfExecKind::Backing,
 		&Default::default(),
 	));
 
@@ -797,7 +811,7 @@ fn candidate_validation_commitment_hash_mismatch_is_invalid() {
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		PvfExecKind::Backing,
 		&Default::default(),
 	))
 	.unwrap();
@@ -846,7 +860,7 @@ fn candidate_validation_code_mismatch_is_invalid() {
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		PvfExecKind::Backing,
 		&Default::default(),
 	))
 	.unwrap();
@@ -903,7 +917,7 @@ fn compressed_code_works() {
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		PvfExecKind::Backing,
 		&Default::default(),
 	));
 
@@ -954,7 +968,7 @@ fn code_decompression_failure_is_error() {
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		PvfExecKind::Backing,
 		&Default::default(),
 	));
 
@@ -1006,7 +1020,7 @@ fn pov_decompression_failure_is_invalid() {
 		candidate_receipt,
 		Arc::new(pov),
 		ExecutorParams::default(),
-		PvfExecTimeoutKind::Backing,
+		PvfExecKind::Backing,
 		&Default::default(),
 	));
 
diff --git a/polkadot/node/core/dispute-coordinator/src/participation/mod.rs b/polkadot/node/core/dispute-coordinator/src/participation/mod.rs
index 90268516e9df01b021ca914ffe59a19b0893bc80..05ea7323af1419d770cb7b68e15ee7887ceeaab8 100644
--- a/polkadot/node/core/dispute-coordinator/src/participation/mod.rs
+++ b/polkadot/node/core/dispute-coordinator/src/participation/mod.rs
@@ -32,7 +32,7 @@ use polkadot_node_subsystem::{
 };
 use polkadot_node_subsystem_util::runtime::get_validation_code_by_hash;
 use polkadot_primitives::{
-	BlockNumber, CandidateHash, CandidateReceipt, Hash, PvfExecTimeoutKind, SessionIndex,
+	BlockNumber, CandidateHash, CandidateReceipt, Hash, PvfExecKind, SessionIndex,
 };
 
 use crate::LOG_TARGET;
@@ -386,7 +386,7 @@ async fn participate(
 			candidate_receipt: req.candidate_receipt().clone(),
 			pov: available_data.pov,
 			executor_params: req.executor_params(),
-			exec_timeout_kind: PvfExecTimeoutKind::Approval,
+			exec_kind: PvfExecKind::Approval,
 			response_sender: validation_tx,
 		})
 		.await;
diff --git a/polkadot/node/core/dispute-coordinator/src/participation/tests.rs b/polkadot/node/core/dispute-coordinator/src/participation/tests.rs
index 0aa0d772005112eac9d3580eab8bcab1985013cd..012df51d0cd3eac291e17354a45ae18e347b121d 100644
--- a/polkadot/node/core/dispute-coordinator/src/participation/tests.rs
+++ b/polkadot/node/core/dispute-coordinator/src/participation/tests.rs
@@ -115,8 +115,8 @@ pub async fn participation_full_happy_path(
 	assert_matches!(
 	ctx_handle.recv().await,
 	AllMessages::CandidateValidation(
-		CandidateValidationMessage::ValidateFromExhaustive { candidate_receipt, exec_timeout_kind, response_sender, .. }
-		) if exec_timeout_kind == PvfExecTimeoutKind::Approval => {
+		CandidateValidationMessage::ValidateFromExhaustive { candidate_receipt, exec_kind, response_sender, .. }
+		) if exec_kind == PvfExecKind::Approval => {
 			if expected_commitments_hash != candidate_receipt.commitments_hash {
 				response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::CommitmentsHashMismatch))).unwrap();
 			} else {
@@ -450,8 +450,8 @@ fn cast_invalid_vote_if_validation_fails_or_is_invalid() {
 		assert_matches!(
 			ctx_handle.recv().await,
 			AllMessages::CandidateValidation(
-				CandidateValidationMessage::ValidateFromExhaustive { exec_timeout_kind, response_sender, .. }
-			) if exec_timeout_kind == PvfExecTimeoutKind::Approval => {
+				CandidateValidationMessage::ValidateFromExhaustive { exec_kind, response_sender, .. }
+			) if exec_kind == PvfExecKind::Approval => {
 				response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::Timeout))).unwrap();
 			},
 			"overseer did not receive candidate validation message",
@@ -487,8 +487,8 @@ fn cast_invalid_vote_if_commitments_dont_match() {
 		assert_matches!(
 			ctx_handle.recv().await,
 			AllMessages::CandidateValidation(
-				CandidateValidationMessage::ValidateFromExhaustive { exec_timeout_kind, response_sender, .. }
-			) if exec_timeout_kind == PvfExecTimeoutKind::Approval => {
+				CandidateValidationMessage::ValidateFromExhaustive { exec_kind, response_sender, .. }
+			) if exec_kind == PvfExecKind::Approval => {
 				response_sender.send(Ok(ValidationResult::Invalid(InvalidCandidate::CommitmentsHashMismatch))).unwrap();
 			},
 			"overseer did not receive candidate validation message",
@@ -524,8 +524,8 @@ fn cast_valid_vote_if_validation_passes() {
 		assert_matches!(
 			ctx_handle.recv().await,
 			AllMessages::CandidateValidation(
-				CandidateValidationMessage::ValidateFromExhaustive { exec_timeout_kind, response_sender, .. }
-			) if exec_timeout_kind == PvfExecTimeoutKind::Approval => {
+				CandidateValidationMessage::ValidateFromExhaustive { exec_kind, response_sender, .. }
+			) if exec_kind == PvfExecKind::Approval => {
 				response_sender.send(Ok(ValidationResult::Valid(dummy_candidate_commitments(None), PersistedValidationData::default()))).unwrap();
 			},
 			"overseer did not receive candidate validation message",
diff --git a/polkadot/node/malus/src/variants/common.rs b/polkadot/node/malus/src/variants/common.rs
index 474887ee8df764a7c1830fbd7b6fe08bfc9f9d4c..20b6654638e7d389d6062a728597386b8f23b5ed 100644
--- a/polkadot/node/malus/src/variants/common.rs
+++ b/polkadot/node/malus/src/variants/common.rs
@@ -30,7 +30,7 @@ use polkadot_node_subsystem::{
 
 use polkadot_primitives::{
 	CandidateCommitments, CandidateDescriptor, CandidateReceipt, PersistedValidationData,
-	PvfExecTimeoutKind,
+	PvfExecKind,
 };
 
 use futures::channel::oneshot;
@@ -90,10 +90,10 @@ impl FakeCandidateValidation {
 		}
 	}
 
-	fn should_misbehave(&self, timeout: PvfExecTimeoutKind) -> bool {
+	fn should_misbehave(&self, timeout: PvfExecKind) -> bool {
 		match timeout {
-			PvfExecTimeoutKind::Backing => self.includes_backing(),
-			PvfExecTimeoutKind::Approval => self.includes_approval(),
+			PvfExecKind::Backing => self.includes_backing(),
+			PvfExecKind::Approval => self.includes_approval(),
 		}
 	}
 }
@@ -279,13 +279,13 @@ where
 						candidate_receipt,
 						pov,
 						executor_params,
-						exec_timeout_kind,
+						exec_kind,
 						response_sender,
 						..
 					},
 			} => {
 				match self.fake_validation {
-					x if x.misbehaves_valid() && x.should_misbehave(exec_timeout_kind) => {
+					x if x.misbehaves_valid() && x.should_misbehave(exec_kind) => {
 						// Behave normally if the `PoV` is not known to be malicious.
 						if pov.block_data.0.as_slice() != MALICIOUS_POV {
 							return Some(FromOrchestra::Communication {
@@ -295,7 +295,7 @@ where
 									candidate_receipt,
 									pov,
 									executor_params,
-									exec_timeout_kind,
+									exec_kind,
 									response_sender,
 								},
 							})
@@ -333,14 +333,14 @@ where
 										candidate_receipt,
 										pov,
 										executor_params,
-										exec_timeout_kind,
+										exec_kind,
 										response_sender,
 									},
 								})
 							},
 						}
 					},
-					x if x.misbehaves_invalid() && x.should_misbehave(exec_timeout_kind) => {
+					x if x.misbehaves_invalid() && x.should_misbehave(exec_kind) => {
 						// Set the validation result to invalid with probability `p` and trigger a
 						// dispute
 						let behave_maliciously = self.distribution.sample(&mut rand::thread_rng());
@@ -373,7 +373,7 @@ where
 										candidate_receipt,
 										pov,
 										executor_params,
-										exec_timeout_kind,
+										exec_kind,
 										response_sender,
 									},
 								})
@@ -388,7 +388,7 @@ where
 							candidate_receipt,
 							pov,
 							executor_params,
-							exec_timeout_kind,
+							exec_kind,
 							response_sender,
 						},
 					}),
@@ -401,13 +401,13 @@ where
 						candidate_receipt,
 						pov,
 						executor_params,
-						exec_timeout_kind,
+						exec_kind,
 						response_sender,
 						..
 					},
 			} => {
 				match self.fake_validation {
-					x if x.misbehaves_valid() && x.should_misbehave(exec_timeout_kind) => {
+					x if x.misbehaves_valid() && x.should_misbehave(exec_kind) => {
 						// Behave normally if the `PoV` is not known to be malicious.
 						if pov.block_data.0.as_slice() != MALICIOUS_POV {
 							return Some(FromOrchestra::Communication {
@@ -415,7 +415,7 @@ where
 									candidate_receipt,
 									pov,
 									executor_params,
-									exec_timeout_kind,
+									exec_kind,
 									response_sender,
 								},
 							})
@@ -445,13 +445,13 @@ where
 									candidate_receipt,
 									pov,
 									executor_params,
-									exec_timeout_kind,
+									exec_kind,
 									response_sender,
 								},
 							}),
 						}
 					},
-					x if x.misbehaves_invalid() && x.should_misbehave(exec_timeout_kind) => {
+					x if x.misbehaves_invalid() && x.should_misbehave(exec_kind) => {
 						// Maliciously set the validation result to invalid for a valid candidate
 						// with probability `p`
 						let behave_maliciously = self.distribution.sample(&mut rand::thread_rng());
@@ -479,7 +479,7 @@ where
 										candidate_receipt,
 										pov,
 										executor_params,
-										exec_timeout_kind,
+										exec_kind,
 										response_sender,
 									},
 								})
@@ -491,7 +491,7 @@ where
 							candidate_receipt,
 							pov,
 							executor_params,
-							exec_timeout_kind,
+							exec_kind,
 							response_sender,
 						},
 					}),
diff --git a/polkadot/node/overseer/examples/minimal-example.rs b/polkadot/node/overseer/examples/minimal-example.rs
index b2c0ea2f75a816c0ccf6f96187d3f48a13102ccc..857cdba673db267d2824d8a995257316fc67225f 100644
--- a/polkadot/node/overseer/examples/minimal-example.rs
+++ b/polkadot/node/overseer/examples/minimal-example.rs
@@ -32,7 +32,7 @@ use polkadot_overseer::{
 	gen::{FromOrchestra, SpawnedSubsystem},
 	HeadSupportsParachains, SubsystemError,
 };
-use polkadot_primitives::{CandidateReceipt, Hash, PvfExecTimeoutKind};
+use polkadot_primitives::{CandidateReceipt, Hash, PvfExecKind};
 
 struct AlwaysSupportsParachains;
 
@@ -77,7 +77,7 @@ impl Subsystem1 {
 				candidate_receipt,
 				pov: PoV { block_data: BlockData(Vec::new()) }.into(),
 				executor_params: Default::default(),
-				exec_timeout_kind: PvfExecTimeoutKind::Backing,
+				exec_kind: PvfExecKind::Backing,
 				response_sender: tx,
 			};
 			ctx.send_message(msg).await;
diff --git a/polkadot/node/overseer/src/tests.rs b/polkadot/node/overseer/src/tests.rs
index 254f5fe4512019acb9cdb3be211e5f88774ae9c4..0494274367d953146ad06e93c3ee00cfc9c3b983 100644
--- a/polkadot/node/overseer/src/tests.rs
+++ b/polkadot/node/overseer/src/tests.rs
@@ -30,7 +30,7 @@ use polkadot_node_subsystem_types::messages::{
 };
 use polkadot_primitives::{
 	CandidateHash, CandidateReceipt, CollatorPair, Id as ParaId, InvalidDisputeStatementKind,
-	PvfExecTimeoutKind, SessionIndex, ValidDisputeStatementKind, ValidatorIndex,
+	PvfExecKind, SessionIndex, ValidDisputeStatementKind, ValidatorIndex,
 };
 
 use crate::{
@@ -106,7 +106,7 @@ where
 							candidate_receipt,
 							pov: PoV { block_data: BlockData(Vec::new()) }.into(),
 							executor_params: Default::default(),
-							exec_timeout_kind: PvfExecTimeoutKind::Backing,
+							exec_kind: PvfExecKind::Backing,
 							response_sender: tx,
 						})
 						.await;
@@ -804,7 +804,7 @@ fn test_candidate_validation_msg() -> CandidateValidationMessage {
 		candidate_receipt,
 		pov,
 		executor_params: Default::default(),
-		exec_timeout_kind: PvfExecTimeoutKind::Backing,
+		exec_kind: PvfExecKind::Backing,
 		response_sender,
 	}
 }
diff --git a/polkadot/node/subsystem-types/src/messages.rs b/polkadot/node/subsystem-types/src/messages.rs
index 43456daec302917471b4e1df1aa5234ad993f892..44c6f27b17cca227b4517c4d6ff47cb3e13f05db 100644
--- a/polkadot/node/subsystem-types/src/messages.rs
+++ b/polkadot/node/subsystem-types/src/messages.rs
@@ -47,7 +47,7 @@ use polkadot_primitives::{
 	CommittedCandidateReceipt, CoreState, DisputeState, ExecutorParams, GroupIndex,
 	GroupRotationInfo, Hash, Header as BlockHeader, Id as ParaId, InboundDownwardMessage,
 	InboundHrmpMessage, MultiDisputeStatementSet, OccupiedCoreAssumption, PersistedValidationData,
-	PvfCheckStatement, PvfExecTimeoutKind, SessionIndex, SessionInfo, SignedAvailabilityBitfield,
+	PvfCheckStatement, PvfExecKind, SessionIndex, SessionInfo, SignedAvailabilityBitfield,
 	SignedAvailabilityBitfields, ValidationCode, ValidationCodeHash, ValidatorId, ValidatorIndex,
 	ValidatorSignature,
 };
@@ -150,8 +150,8 @@ pub enum CandidateValidationMessage {
 		pov: Arc<PoV>,
 		/// Session's executor parameters
 		executor_params: ExecutorParams,
-		/// Execution timeout kind (backing/approvals)
-		exec_timeout_kind: PvfExecTimeoutKind,
+		/// Execution kind, used for timeouts and retries (backing/approvals)
+		exec_kind: PvfExecKind,
 		/// The sending side of the response channel
 		response_sender: oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
 	},
@@ -175,8 +175,8 @@ pub enum CandidateValidationMessage {
 		pov: Arc<PoV>,
 		/// Session's executor parameters
 		executor_params: ExecutorParams,
-		/// Execution timeout kind (backing/approvals)
-		exec_timeout_kind: PvfExecTimeoutKind,
+		/// Execution kind, used for timeouts and retries (backing/approvals)
+		exec_kind: PvfExecKind,
 		/// The sending side of the response channel
 		response_sender: oneshot::Sender<Result<ValidationResult, ValidationFailed>>,
 	},
diff --git a/polkadot/primitives/src/lib.rs b/polkadot/primitives/src/lib.rs
index 4ba8b8b031fcc330bb66d1ec4bd329809958db07..2570bcadf606ab8ef0809a1f258a6068263f1db1 100644
--- a/polkadot/primitives/src/lib.rs
+++ b/polkadot/primitives/src/lib.rs
@@ -48,11 +48,11 @@ pub use v6::{
 	HorizontalMessages, HrmpChannelId, Id, InboundDownwardMessage, InboundHrmpMessage, IndexedVec,
 	InherentData, InvalidDisputeStatementKind, Moment, MultiDisputeStatementSet, Nonce,
 	OccupiedCore, OccupiedCoreAssumption, OutboundHrmpMessage, ParathreadClaim, ParathreadEntry,
-	PersistedValidationData, PvfCheckStatement, PvfExecTimeoutKind, PvfPrepTimeoutKind,
-	RuntimeMetricLabel, RuntimeMetricLabelValue, RuntimeMetricLabelValues, RuntimeMetricLabels,
-	RuntimeMetricOp, RuntimeMetricUpdate, ScheduledCore, ScrapedOnChainVotes, SessionIndex,
-	SessionInfo, Signature, Signed, SignedAvailabilityBitfield, SignedAvailabilityBitfields,
-	SignedStatement, SigningContext, Slot, UncheckedSigned, UncheckedSignedAvailabilityBitfield,
+	PersistedValidationData, PvfCheckStatement, PvfExecKind, PvfPrepKind, RuntimeMetricLabel,
+	RuntimeMetricLabelValue, RuntimeMetricLabelValues, RuntimeMetricLabels, RuntimeMetricOp,
+	RuntimeMetricUpdate, ScheduledCore, ScrapedOnChainVotes, SessionIndex, SessionInfo, Signature,
+	Signed, SignedAvailabilityBitfield, SignedAvailabilityBitfields, SignedStatement,
+	SigningContext, Slot, UncheckedSigned, UncheckedSignedAvailabilityBitfield,
 	UncheckedSignedAvailabilityBitfields, UncheckedSignedStatement, UpgradeGoAhead,
 	UpgradeRestriction, UpwardMessage, ValidDisputeStatementKind, ValidationCode,
 	ValidationCodeHash, ValidatorId, ValidatorIndex, ValidatorSignature, ValidityAttestation,
diff --git a/polkadot/primitives/src/v6/executor_params.rs b/polkadot/primitives/src/v6/executor_params.rs
index bb9980f68796297848ad216cf12de4cf0084b8e9..112a529f62b0570e9270d1f09e397e6b9dc358b0 100644
--- a/polkadot/primitives/src/v6/executor_params.rs
+++ b/polkadot/primitives/src/v6/executor_params.rs
@@ -21,7 +21,7 @@
 //! by the first element of the vector). Decoding to a usable semantics structure is
 //! done in `polkadot-node-core-pvf`.
 
-use crate::{BlakeTwo256, HashT as _, PvfExecTimeoutKind, PvfPrepTimeoutKind};
+use crate::{BlakeTwo256, HashT as _, PvfExecKind, PvfPrepKind};
 use parity_scale_codec::{Decode, Encode};
 use polkadot_core_primitives::Hash;
 use scale_info::TypeInfo;
@@ -45,7 +45,7 @@ pub const PRECHECK_MEM_MAX_LO: u64 = 256 * 1024 * 1024;
 pub const PRECHECK_MEM_MAX_HI: u64 = 16 * 1024 * 1024 * 1024;
 
 // Default PVF timeouts. Must never be changed! Use executor environment parameters to adjust them.
-// See also `PvfPrepTimeoutKind` and `PvfExecTimeoutKind` docs.
+// See also `PvfPrepKind` and `PvfExecKind` docs.
 
 /// Default PVF preparation timeout for prechecking requests.
 pub const DEFAULT_PRECHECK_PREPARATION_TIMEOUT: Duration = Duration::from_secs(60);
@@ -99,12 +99,12 @@ pub enum ExecutorParam {
 	/// Always ensure that `precheck_timeout` < `lenient_timeout`.
 	/// When absent, the default values will be used.
 	#[codec(index = 5)]
-	PvfPrepTimeout(PvfPrepTimeoutKind, u64),
+	PvfPrepTimeout(PvfPrepKind, u64),
 	/// PVF execution timeouts, in millisecond.
 	/// Always ensure that `backing_timeout` < `approval_timeout`.
 	/// When absent, the default values will be used.
 	#[codec(index = 6)]
-	PvfExecTimeout(PvfExecTimeoutKind, u64),
+	PvfExecTimeout(PvfExecKind, u64),
 	/// Enables WASM bulk memory proposal
 	#[codec(index = 7)]
 	WasmExtBulkMemory,
@@ -174,7 +174,7 @@ impl ExecutorParams {
 	}
 
 	/// Returns a PVF preparation timeout, if any
-	pub fn pvf_prep_timeout(&self, kind: PvfPrepTimeoutKind) -> Option<Duration> {
+	pub fn pvf_prep_timeout(&self, kind: PvfPrepKind) -> Option<Duration> {
 		for param in &self.0 {
 			if let ExecutorParam::PvfPrepTimeout(k, timeout) = param {
 				if kind == *k {
@@ -186,7 +186,7 @@ impl ExecutorParams {
 	}
 
 	/// Returns a PVF execution timeout, if any
-	pub fn pvf_exec_timeout(&self, kind: PvfExecTimeoutKind) -> Option<Duration> {
+	pub fn pvf_exec_timeout(&self, kind: PvfExecKind) -> Option<Duration> {
 		for param in &self.0 {
 			if let ExecutorParam::PvfExecTimeout(k, timeout) = param {
 				if kind == *k {
@@ -242,12 +242,12 @@ impl ExecutorParams {
 				StackNativeMax(_) => "StackNativeMax",
 				PrecheckingMaxMemory(_) => "PrecheckingMaxMemory",
 				PvfPrepTimeout(kind, _) => match kind {
-					PvfPrepTimeoutKind::Precheck => "PvfPrepTimeoutKind::Precheck",
-					PvfPrepTimeoutKind::Lenient => "PvfPrepTimeoutKind::Lenient",
+					PvfPrepKind::Precheck => "PvfPrepKind::Precheck",
+					PvfPrepKind::Prepare => "PvfPrepKind::Prepare",
 				},
 				PvfExecTimeout(kind, _) => match kind {
-					PvfExecTimeoutKind::Backing => "PvfExecTimeoutKind::Backing",
-					PvfExecTimeoutKind::Approval => "PvfExecTimeoutKind::Approval",
+					PvfExecKind::Backing => "PvfExecKind::Backing",
+					PvfExecKind::Approval => "PvfExecKind::Approval",
 				},
 				WasmExtBulkMemory => "WasmExtBulkMemory",
 			};
@@ -297,30 +297,23 @@ impl ExecutorParams {
 		}
 
 		if let (Some(precheck), Some(lenient)) = (
-			seen.get("PvfPrepTimeoutKind::Precheck")
+			seen.get("PvfPrepKind::Precheck")
 				.or(Some(&DEFAULT_PRECHECK_PREPARATION_TIMEOUT_MS)),
-			seen.get("PvfPrepTimeoutKind::Lenient")
+			seen.get("PvfPrepKind::Prepare")
 				.or(Some(&DEFAULT_LENIENT_PREPARATION_TIMEOUT_MS)),
 		) {
 			if *precheck >= *lenient {
-				return Err(IncompatibleValues(
-					"PvfPrepTimeoutKind::Precheck",
-					"PvfPrepTimeoutKind::Lenient",
-				))
+				return Err(IncompatibleValues("PvfPrepKind::Precheck", "PvfPrepKind::Prepare"))
 			}
 		}
 
 		if let (Some(backing), Some(approval)) = (
-			seen.get("PvfExecTimeoutKind::Backing")
-				.or(Some(&DEFAULT_BACKING_EXECUTION_TIMEOUT_MS)),
-			seen.get("PvfExecTimeoutKind::Approval")
+			seen.get("PvfExecKind::Backing").or(Some(&DEFAULT_BACKING_EXECUTION_TIMEOUT_MS)),
+			seen.get("PvfExecKind::Approval")
 				.or(Some(&DEFAULT_APPROVAL_EXECUTION_TIMEOUT_MS)),
 		) {
 			if *backing >= *approval {
-				return Err(IncompatibleValues(
-					"PvfExecTimeoutKind::Backing",
-					"PvfExecTimeoutKind::Approval",
-				))
+				return Err(IncompatibleValues("PvfExecKind::Backing", "PvfExecKind::Approval"))
 			}
 		}
 
diff --git a/polkadot/primitives/src/v6/mod.rs b/polkadot/primitives/src/v6/mod.rs
index 9371b3db406b3e6247f4ac9c12dc5ba053a77f01..83b590dc32032c4c366ea0f8723f1cc76837effa 100644
--- a/polkadot/primitives/src/v6/mod.rs
+++ b/polkadot/primitives/src/v6/mod.rs
@@ -1781,30 +1781,22 @@ impl<T: Encode> WellKnownKey<T> {
 	}
 }
 
-/// Type discriminator for PVF preparation timeouts
+/// Type discriminator for PVF preparation.
 #[derive(Encode, Decode, TypeInfo, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
-pub enum PvfPrepTimeoutKind {
-	/// For prechecking requests, the time period after which the preparation worker is considered
-	/// unresponsive and will be killed.
+pub enum PvfPrepKind {
+	/// For prechecking requests.
 	Precheck,
 
-	/// For execution and heads-up requests, the time period after which the preparation worker is
-	/// considered unresponsive and will be killed. More lenient than the timeout for prechecking
-	/// to prevent honest validators from timing out on valid PVFs.
-	Lenient,
+	/// For execution and heads-up requests.
+	Prepare,
 }
 
-/// Type discriminator for PVF execution timeouts
+/// Type discriminator for PVF execution.
 #[derive(Encode, Decode, TypeInfo, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
-pub enum PvfExecTimeoutKind {
-	/// The amount of time to spend on execution during backing.
+pub enum PvfExecKind {
+	/// For backing requests.
 	Backing,
-
-	/// The amount of time to spend on execution during approval or disputes.
-	///
-	/// This should be much longer than the backing execution timeout to ensure that in the
-	/// absence of extremely large disparities between hardware, blocks that pass backing are
-	/// considered executable by approval checkers or dispute participants.
+	/// For approval and dispute request.
 	Approval,
 }
 
diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md
index 4dbb7980c1be36c3750cf78c74aabc932a3fd7fc..74d88ba3ad99295cf434bd33bbe819c53ae66399 100644
--- a/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md
+++ b/polkadot/roadmap/implementers-guide/src/node/utility/pvf-host-and-workers.md
@@ -31,6 +31,10 @@ hopefully resolve. We use a more brief delay here (1 second as opposed to 15
 minutes for preparation (see above)), because a successful execution must happen
 in a short amount of time.
 
+If the execution fails during the backing phase, we won't retry to reduce the chance of
+supporting nondeterministic candidates. This reduces the chance of nondeterministic blocks
+getting backed and honest backers getting slashed.
+
 We currently know of the following specific cases that will lead to a retried
 execution request:
 
diff --git a/polkadot/runtime/parachains/src/configuration/benchmarking.rs b/polkadot/runtime/parachains/src/configuration/benchmarking.rs
index 508e0579a09dacbb200f01ccb571340f582b7dcd..67daf1c459884d42378056b6528605da29578c95 100644
--- a/polkadot/runtime/parachains/src/configuration/benchmarking.rs
+++ b/polkadot/runtime/parachains/src/configuration/benchmarking.rs
@@ -17,7 +17,7 @@
 use crate::configuration::*;
 use frame_benchmarking::{benchmarks, BenchmarkError, BenchmarkResult};
 use frame_system::RawOrigin;
-use primitives::{ExecutorParam, ExecutorParams, PvfExecTimeoutKind, PvfPrepTimeoutKind};
+use primitives::{ExecutorParam, ExecutorParams, PvfExecKind, PvfPrepKind};
 use sp_runtime::traits::One;
 
 benchmarks! {
@@ -41,10 +41,10 @@ benchmarks! {
 		ExecutorParam::StackNativeMax(256 * 1024 * 1024),
 		ExecutorParam::WasmExtBulkMemory,
 		ExecutorParam::PrecheckingMaxMemory(2 * 1024 * 1024 * 1024),
-		ExecutorParam::PvfPrepTimeout(PvfPrepTimeoutKind::Precheck, 60_000),
-		ExecutorParam::PvfPrepTimeout(PvfPrepTimeoutKind::Lenient, 360_000),
-		ExecutorParam::PvfExecTimeout(PvfExecTimeoutKind::Backing, 2_000),
-		ExecutorParam::PvfExecTimeout(PvfExecTimeoutKind::Approval, 12_000),
+		ExecutorParam::PvfPrepTimeout(PvfPrepKind::Precheck, 60_000),
+		ExecutorParam::PvfPrepTimeout(PvfPrepKind::Prepare, 360_000),
+		ExecutorParam::PvfExecTimeout(PvfExecKind::Backing, 2_000),
+		ExecutorParam::PvfExecTimeout(PvfExecKind::Approval, 12_000),
 	][..]))
 
 	set_config_with_perbill {}: set_on_demand_fee_variability(RawOrigin::Root, Perbill::from_percent(100))