diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 1610c60ec4b..437080a0a41 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -1913,6 +1913,40 @@ TableToProcessComparator(const ListCell *a, const ListCell *b)
 	return (t2->score < t1->score) ? -1 : (t2->score > t1->score) ? 1 : 0;
 }
 
+/*
+ * get_effective_relopts
+ *		Fetch the storage parameters that apply to a relation.
+ *
+ * This looks up the reloptions for the pg_class relation in "tup".  If it is a
+ * TOAST table, we also merge in any unset reloptions with the main table's
+ * stored in "toast_map".  If the relation neither sets nor inherits any
+ * reloptions, this function returns NULL.  Else, a palloc'd copy of the
+ * applicable reloptions are returned.
+ *
+ * If "tup" refers to a TOAST table and the toast_map has reloptions stored for
+ * its main relation, we return a pointer to the main table's reloptions via
+ * *main_opts.  Else, main_opts is set to NULL.
+ */
+static StdRdOptions *
+get_effective_relopts(HeapTuple tup, TupleDesc desc, HTAB *toast_map,
+					  StdRdOptions **main_opts)
+{
+	Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tup);
+	StdRdOptions *relopts;
+	av_relation *hentry = NULL;
+
+	/* look up our relopts */
+	relopts = (StdRdOptions *) extractRelOptions(tup, desc, NULL);
+
+	/* if we're a TOAST table, look up our parent's relopts, too */
+	if (classForm->relkind == RELKIND_TOASTVALUE)
+		hentry = hash_search(toast_map, &classForm->oid, HASH_FIND, NULL);
+	*main_opts = hentry ? &hentry->ar_reloptions : NULL;
+
+	/* return the merged reloptions */
+	return merge_toast_reloptions(relopts, *main_opts);
+}
+
 /*
  * Process a database table-by-table
  *
@@ -2015,9 +2049,9 @@ do_autovacuum(void)
 	 * We do this in two passes: on the first one we collect the list of plain
 	 * relations and materialized views, and on the second one we collect
 	 * TOAST tables. The reason for doing the second pass is that during it we
-	 * want to use the main relation's pg_class.reloptions entry if the TOAST
-	 * table does not have any, and we cannot obtain it unless we know
-	 * beforehand what's the main table OID.
+	 * want to fill in any storage parameters that the TOAST table does not
+	 * set with the main relation's, and we cannot obtain those values unless
+	 * we know beforehand what's the main table OID.
 	 *
 	 * We need to check TOAST tables separately because in cases with short,
 	 * wide tables there might be proportionally much more activity in the
@@ -2128,7 +2162,7 @@ do_autovacuum(void)
 		Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
 		Oid			relid;
 		StdRdOptions *relopts;
-		bool		free_relopts = false;
+		StdRdOptions *main_relopts;
 		bool		dovacuum;
 		bool		doanalyze;
 		bool		wraparound;
@@ -2142,22 +2176,9 @@ do_autovacuum(void)
 
 		relid = classForm->oid;
 
-		/*
-		 * fetch reloptions -- if this toast table does not have them, try the
-		 * main rel
-		 */
-		relopts = (StdRdOptions *) extractRelOptions(tuple, pg_class_desc, NULL);
-		if (relopts)
-			free_relopts = true;
-		else
-		{
-			av_relation *hentry;
-			bool		found;
-
-			hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found);
-			if (found)
-				relopts = &hentry->ar_reloptions;
-		}
+		/* fetch reloptions -- merge any unset options from the main rel */
+		relopts = get_effective_relopts(tuple, pg_class_desc, table_toast_map,
+										&main_relopts);
 
 		relation_needs_vacanalyze(relid, relopts, classForm,
 								  effective_multixact_freeze_max_age,
@@ -2176,7 +2197,7 @@ do_autovacuum(void)
 		}
 
 		/* Release stuff to avoid leakage */
-		if (free_relopts)
+		if (relopts)
 			pfree(relopts);
 	}
 
@@ -2782,7 +2803,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	autovac_table *tab = NULL;
 	bool		wraparound;
 	StdRdOptions *relopts;
-	bool		free_relopts = false;
+	StdRdOptions *main_relopts;
 	AutoVacuumScores scores;
 
 	/* fetch the relation's relcache entry */
@@ -2792,21 +2813,11 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 	classForm = (Form_pg_class) GETSTRUCT(classTup);
 
 	/*
-	 * Get the applicable reloptions.  If it is a TOAST table, try to get the
-	 * main table reloptions if the toast table itself doesn't have.
+	 * Get the applicable reloptions.  If it is a TOAST table, merge in the
+	 * main table's reloptions where they are unset.
 	 */
-	relopts = (StdRdOptions *) extractRelOptions(classTup, pg_class_desc, NULL);
-	if (relopts)
-		free_relopts = true;
-	else if (classForm->relkind == RELKIND_TOASTVALUE)
-	{
-		av_relation *hentry;
-		bool		found;
-
-		hentry = hash_search(table_toast_map, &relid, HASH_FIND, &found);
-		if (found)
-			relopts = &hentry->ar_reloptions;
-	}
+	relopts = get_effective_relopts(classTup, pg_class_desc, table_toast_map,
+									&main_relopts);
 
 	relation_needs_vacanalyze(relid, relopts, classForm,
 							  effective_multixact_freeze_max_age,
@@ -2891,7 +2902,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 		tab->at_params.log_vacuum_min_duration = log_vacuum_min_duration;
 		tab->at_params.log_analyze_min_duration = log_analyze_min_duration;
 		tab->at_params.toast_parent = InvalidOid;
-		tab->at_params.main_relopts = NULL;
+		tab->at_params.main_relopts = main_relopts;
 
 		/* Determine the number of parallel vacuum workers to use */
 		tab->at_params.nworkers = 0;
@@ -2936,7 +2947,7 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
 						 avopts->vacuum_cost_delay >= 0));
 	}
 
-	if (free_relopts)
+	if (relopts)
 		pfree(relopts);
 	heap_freetuple(classTup);
 	return tab;
@@ -2950,8 +2961,8 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
  * being forced because of Xid or multixact wraparound.
  *
  * relopts is a pointer to the StdRdOptions options (either for itself in the
- * case of a plain table, or for either itself or its parent table in the case
- * of a TOAST table), NULL if none.
+ * case of a plain table, or merged with the main table's for a TOAST table),
+ * NULL if none.
  *
  * A table needs to be vacuumed if the number of dead tuples exceeds a
  * threshold.  This threshold is calculated as
@@ -3609,6 +3620,8 @@ pg_stat_get_autovacuum_scores(PG_FUNCTION_ARGS)
 	TableScanDesc scan;
 	HeapTuple	tup;
 	ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+	HTAB	   *table_toast_map;
+	HASHCTL		ctl;
 
 	InitMaterializedSRF(fcinfo, 0);
 
@@ -3617,13 +3630,62 @@ pg_stat_get_autovacuum_scores(PG_FUNCTION_ARGS)
 	recentXid = ReadNextTransactionId();
 	recentMulti = ReadNextMultiXactId();
 
-	/* scan pg_class */
+	/* create hash table for toast <-> main relid mapping */
+	ctl.keysize = sizeof(Oid);
+	ctl.entrysize = sizeof(av_relation);
+	ctl.hcxt = CurrentMemoryContext;
+	table_toast_map = hash_create("TOAST to main relid map",
+								  100,
+								  &ctl,
+								  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
+
+	/*
+	 * Do an initial pass over pg_class to collect the main relations'
+	 * reloptions, which we need in order to compute their TOAST tables'
+	 * effective options below.
+	 */
 	rel = table_open(RelationRelationId, AccessShareLock);
 	scan = table_beginscan_catalog(rel, 0, NULL);
 	while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
 		Form_pg_class form = (Form_pg_class) GETSTRUCT(tup);
 		StdRdOptions *relopts;
+		av_relation *hentry;
+		bool		found;
+
+		/* skip ineligible entries */
+		if (form->relkind != RELKIND_RELATION &&
+			form->relkind != RELKIND_MATVIEW)
+			continue;
+		if (form->relpersistence == RELPERSISTENCE_TEMP)
+			continue;
+		if (!OidIsValid(form->reltoastrelid))
+			continue;
+
+		relopts = (StdRdOptions *) extractRelOptions(tup, RelationGetDescr(rel), NULL);
+		if (!relopts)
+			continue;
+
+		hentry = hash_search(table_toast_map, &form->reltoastrelid,
+							 HASH_ENTER, &found);
+		Assert(!found);			/* rels cannot share a TOAST table */
+
+		/* hash_search already filled in the key */
+		memcpy(&hentry->ar_reloptions, relopts, sizeof(StdRdOptions));
+
+		pfree(relopts);
+	}
+	table_endscan(scan);
+
+	/*
+	 * Now that we have all parents' reloptions, we can generate the results.
+	 */
+	scan = table_beginscan_catalog(rel, 0, NULL);
+	while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
+	{
+		Form_pg_class form = (Form_pg_class) GETSTRUCT(tup);
+		StdRdOptions *relopts;
+		StdRdOptions *main_relopts;
 		bool		dovacuum;
 		bool		doanalyze;
 		bool		wraparound;
@@ -3639,7 +3701,9 @@ pg_stat_get_autovacuum_scores(PG_FUNCTION_ARGS)
 		if (form->relpersistence == RELPERSISTENCE_TEMP)
 			continue;
 
-		relopts = (StdRdOptions *) extractRelOptions(tup, RelationGetDescr(rel), NULL);
+		relopts = get_effective_relopts(tup, RelationGetDescr(rel),
+										table_toast_map, &main_relopts);
+
 		relation_needs_vacanalyze(form->oid, relopts, form,
 								  effective_multixact_freeze_max_age,
 								  LOG_NEVER,
@@ -3663,6 +3727,7 @@ pg_stat_get_autovacuum_scores(PG_FUNCTION_ARGS)
 	}
 	table_endscan(scan);
 	table_close(rel, AccessShareLock);
+	hash_destroy(table_toast_map);
 
 	return (Datum) 0;
 }
diff --git a/src/test/modules/test_autovacuum/meson.build b/src/test/modules/test_autovacuum/meson.build
index 86e392bc0de..970b9aaae4b 100644
--- a/src/test/modules/test_autovacuum/meson.build
+++ b/src/test/modules/test_autovacuum/meson.build
@@ -10,6 +10,7 @@ tests += {
     },
     'tests': [
       't/001_parallel_autovacuum.pl',
+      't/002_toast_relopts.pl',
     ],
   },
 }
diff --git a/src/test/modules/test_autovacuum/t/002_toast_relopts.pl b/src/test/modules/test_autovacuum/t/002_toast_relopts.pl
new file mode 100644
index 00000000000..ba7596cc82b
--- /dev/null
+++ b/src/test/modules/test_autovacuum/t/002_toast_relopts.pl
@@ -0,0 +1,69 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+# Test autovacuum's handling of TOAST storage parameters
+
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# Create a test node with autovacuum disabled.
+my $node = PostgreSQL::Test::Cluster->new('main');
+$node->init;
+$node->append_conf(
+	'postgresql.conf', qq{
+autovacuum = off
+autovacuum_naptime = '1s'
+});
+$node->start;
+
+# Create TOAST table that is eligible for autovacuum due to inherited relopts.
+$node->safe_psql(
+	'postgres', qq{
+  CREATE TABLE toast_relopts (i int, j text STORAGE EXTERNAL) WITH
+    (autovacuum_enabled = false, toast.autovacuum_enabled = true,
+     autovacuum_vacuum_threshold = 1,
+     autovacuum_vacuum_scale_factor = 0,
+     autovacuum_vacuum_insert_threshold = 1,
+     autovacuum_vacuum_insert_scale_factor = 0,
+     vacuum_truncate = false);
+  INSERT INTO toast_relopts VALUES (1, repeat('a', 10000)), (2, repeat('b', 10000));
+  SELECT pg_stat_force_next_flush();
+});
+
+# Get TOAST table's OID for following commands.
+my $toast = $node->safe_psql('postgres',
+	"SELECT reltoastrelid::regclass FROM pg_class WHERE oid = 'toast_relopts'::regclass"
+);
+
+# Verify scores view used inherited insert threshold.
+is( $node->safe_psql(
+		'postgres', qq{
+  SELECT vacuum_insert_score > 1 FROM pg_stat_autovacuum_scores
+  WHERE relid = '$toast'::regclass
+}),
+	't',
+	'inherited insert threshold in pg_stat_autovacuum_scores');
+
+# Delete all rows so that we can verify inherited vacuum_truncate takes effect.
+$node->safe_psql('postgres', 'DELETE FROM toast_relopts');
+
+# Enable autovacuum.
+$node->append_conf('postgresql.conf', 'autovacuum = on');
+$node->reload;
+
+# Wait until autovacuum processes the table.
+ok( $node->poll_query_until(
+		'postgres', qq{
+  SELECT last_autovacuum IS NOT NULL FROM pg_stat_all_tables
+  WHERE relid = '$toast'::regclass
+}),
+	'autovacuum of a TOAST table with inherited thresholds');
+
+# Verify autovacuum didn't truncate the table.
+is($node->safe_psql('postgres', "SELECT pg_relation_size('$toast') > 0"),
+	't', 'inherited vacuum_truncate');
+
+$node->stop;
+done_testing();
-- 
2.55.0
