diff --git a/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs b/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs
index 0e3c4ecc29ba2acce65e982a156ba0ced50fccdb..554f79bd0b84f87a072c9c6d01f312d7a4fc50f6 100644
--- a/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs
+++ b/polkadot/node/core/dispute-coordinator/src/scraping/mod.rs
@@ -151,7 +151,10 @@ impl Inclusions {
 /// `process_active_leaves_update` any scraped votes.
 ///
 /// Scraped candidates are available `DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION` more blocks
-/// after finalization as a precaution not to prune them prematurely.
+/// after finalization as a precaution not to prune them prematurely. Besides the newly scraped
+/// candidates `DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION` finalized blocks are parsed as
+/// another precaution to have their `CandidateReceipts` available in case a dispute is raised on
+/// them,
 pub struct ChainScraper {
 	/// All candidates we have seen included, which not yet have been finalized.
 	included_candidates: candidates::ScrapedCandidates,
@@ -228,9 +231,10 @@ impl ChainScraper {
 			None => return Ok(ScrapedUpdates::new()),
 		};
 
-		// Fetch ancestry up to last finalized block.
+		// Fetch ancestry up to `SCRAPED_FINALIZED_BLOCKS_COUNT` blocks beyond
+		// the last finalized one
 		let ancestors = self
-			.get_unfinalized_block_ancestors(sender, activated.hash, activated.number)
+			.get_relevant_block_ancestors(sender, activated.hash, activated.number)
 			.await?;
 
 		// Ancestors block numbers are consecutive in the descending order.
@@ -330,10 +334,11 @@ impl ChainScraper {
 	}
 
 	/// Returns ancestors of `head` in the descending order, stopping
-	/// either at the block present in cache or at the last finalized block.
+	/// either at the block present in cache or at `SCRAPED_FINALIZED_BLOCKS_COUNT -1` blocks after
+	/// the last finalized one (called `target_ancestor`).
 	///
-	/// Both `head` and the latest finalized block are **not** included in the result.
-	async fn get_unfinalized_block_ancestors<Sender>(
+	/// Both `head` and the `target_ancestor` blocks are **not** included in the result.
+	async fn get_relevant_block_ancestors<Sender>(
 		&mut self,
 		sender: &mut Sender,
 		mut head: Hash,
@@ -342,7 +347,9 @@ impl ChainScraper {
 	where
 		Sender: overseer::DisputeCoordinatorSenderTrait,
 	{
-		let target_ancestor = get_finalized_block_number(sender).await?;
+		let target_ancestor = get_finalized_block_number(sender)
+			.await?
+			.saturating_sub(DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION);
 
 		let mut ancestors = Vec::new();
 
diff --git a/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs b/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs
index b7183739d8f8cb40c15b4c01fd7f1c0c39d35a40..da76a93b7353a76aa94f131cc0de535780df79cc 100644
--- a/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs
+++ b/polkadot/node/core/dispute-coordinator/src/scraping/tests.rs
@@ -410,7 +410,8 @@ fn scraper_requests_candidates_of_non_finalized_ancestors() {
 			&mut virtual_overseer,
 			&chain,
 			finalized_block_number,
-			BLOCKS_TO_SKIP - finalized_block_number as usize, // Expect the provider not to go past finalized block.
+			BLOCKS_TO_SKIP -
+				(finalized_block_number - DISPUTE_CANDIDATE_LIFETIME_AFTER_FINALIZATION) as usize, // Expect the provider not to go past finalized block.
 			get_backed_and_included_candidate_events,
 		);
 		join(process_active_leaves_update(ctx.sender(), &mut ordering, next_update), overseer_fut)
diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs
index 18b7fa18a0c82a690abe23d3fc8a864e126d7da4..c113769563ad374bf1ff0dbedfa9f3c51aace08f 100644
--- a/polkadot/node/primitives/src/lib.rs
+++ b/polkadot/node/primitives/src/lib.rs
@@ -65,7 +65,7 @@ pub const VALIDATION_CODE_BOMB_LIMIT: usize = (MAX_CODE_SIZE * 4u32) as usize;
 pub const POV_BOMB_LIMIT: usize = (MAX_POV_SIZE * 4u32) as usize;
 
 /// How many blocks after finalization an information about backed/included candidate should be
-/// kept.
+/// pre-loaded (when scraoing onchain votes) and kept locally (when pruning).
 ///
 /// We don't want to remove scraped candidates on finalization because we want to
 /// be sure that disputes will conclude on abandoned forks.
@@ -74,6 +74,12 @@ pub const POV_BOMB_LIMIT: usize = (MAX_POV_SIZE * 4u32) as usize;
 /// better one gets finalized the entries for the bad fork will be pruned and we
 /// might never participate in a dispute for it.
 ///
+/// Why pre-load finalized blocks? I dispute might be raised against finalized candidate. In most
+/// of the cases it will conclude valid (otherwise we are in big trouble) but never the less the
+/// node must participate. It's possible to see a vote for such dispute onchain before we have it
+/// imported by `dispute-distribution`. In this case we won't have `CandidateReceipt` and the import
+/// will fail unless we keep them preloaded.
+///
 /// This value should consider the timeout we allow for participation in approval-voting. In
 /// particular, the following condition should hold:
 ///