diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index ff0e875f..bb254079 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -167,6 +167,7 @@ static void set_param_references(PlannerInfo *root, Plan *plan);
 static Node *convert_combining_aggrefs(Node *node, void *context);
 static void set_dummy_tlist_references(Plan *plan, int rtoffset);
 static indexed_tlist *build_tlist_index(List *tlist);
+static indexed_tlist *build_tlist_index_other_vars(List *tlist, int ignore_rel);
 static Var *search_indexed_tlist_for_var(Var *var,
 										 indexed_tlist *itlist,
 										 int newvarno,
@@ -1168,7 +1169,8 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 				{
 					indexed_tlist *itlist;
 
-					itlist = build_tlist_index(splan->exclRelTlist);
+					itlist = build_tlist_index_other_vars(splan->exclRelTlist,
+														  0);
 
 					splan->onConflictSet =
 						fix_join_expr(root, splan->onConflictSet,
diff --git a/src/test/regress/expected/generated_virtual.out b/src/test/regress/expected/generated_virtual.out
index fc41c480..8f5ad91b 100644
--- a/src/test/regress/expected/generated_virtual.out
+++ b/src/test/regress/expected/generated_virtual.out
@@ -1723,3 +1723,55 @@ select * from gtest33 where b is null;
 
 reset constraint_exclusion;
 drop table gtest33;
+-- Ensure that EXCLUDED.<virtual-generated-column> in INSERT ... ON CONFLICT
+-- DO UPDATE is expanded to the generation expression, both for plain and
+-- partitioned target relations.  Previously, the plan-time setrefs step
+-- would re-collapse the expanded expression into a Var referring to the
+-- virtual attnum of the EXCLUDED slot, producing either an "unexpected
+-- virtual generated column reference" error (plain table) or a silent NULL
+-- (partitioned table, via tuple routing's attribute remapping).
+create table gtest34 (id int primary key, a int,
+                      c int generated always as (a * 10) virtual);
+insert into gtest34 values (1, 5);
+insert into gtest34 values (1, 7)
+    on conflict (id) do update set a = excluded.c returning *;
+ id | a  |  c  
+----+----+-----
+  1 | 70 | 700
+(1 row)
+
+insert into gtest34 values (1, 2)
+    on conflict (id) do update set a = gtest34.c + excluded.c returning *;
+ id |  a  |  c   
+----+-----+------
+  1 | 720 | 7200
+(1 row)
+
+insert into gtest34 values (1, 3)
+    on conflict (id) do update set a = 999 where excluded.c > 20 returning *;
+ id |  a  |  c   
+----+-----+------
+  1 | 999 | 9990
+(1 row)
+
+drop table gtest34;
+create table gtest34p (id int primary key, a int,
+                       c int generated always as (a * 10) virtual)
+    partition by range (id);
+create table gtest34p_1 partition of gtest34p for values from (1) to (100);
+insert into gtest34p values (1, 5);
+insert into gtest34p values (1, 7)
+    on conflict (id) do update set a = excluded.c returning *;
+ id | a  |  c  
+----+----+-----
+  1 | 70 | 700
+(1 row)
+
+insert into gtest34p values (1, 2)
+    on conflict (id) do update set a = gtest34p.c + excluded.c returning *;
+ id |  a  |  c   
+----+-----+------
+  1 | 720 | 7200
+(1 row)
+
+drop table gtest34p;
diff --git a/src/test/regress/sql/generated_virtual.sql b/src/test/regress/sql/generated_virtual.sql
index 9b32413e..6e958011 100644
--- a/src/test/regress/sql/generated_virtual.sql
+++ b/src/test/regress/sql/generated_virtual.sql
@@ -906,3 +906,32 @@ select * from gtest33 where b is null;
 
 reset constraint_exclusion;
 drop table gtest33;
+
+-- Ensure that EXCLUDED.<virtual-generated-column> in INSERT ... ON CONFLICT
+-- DO UPDATE is expanded to the generation expression, both for plain and
+-- partitioned target relations.  Previously, the plan-time setrefs step
+-- would re-collapse the expanded expression into a Var referring to the
+-- virtual attnum of the EXCLUDED slot, producing either an "unexpected
+-- virtual generated column reference" error (plain table) or a silent NULL
+-- (partitioned table, via tuple routing's attribute remapping).
+create table gtest34 (id int primary key, a int,
+                      c int generated always as (a * 10) virtual);
+insert into gtest34 values (1, 5);
+insert into gtest34 values (1, 7)
+    on conflict (id) do update set a = excluded.c returning *;
+insert into gtest34 values (1, 2)
+    on conflict (id) do update set a = gtest34.c + excluded.c returning *;
+insert into gtest34 values (1, 3)
+    on conflict (id) do update set a = 999 where excluded.c > 20 returning *;
+drop table gtest34;
+
+create table gtest34p (id int primary key, a int,
+                       c int generated always as (a * 10) virtual)
+    partition by range (id);
+create table gtest34p_1 partition of gtest34p for values from (1) to (100);
+insert into gtest34p values (1, 5);
+insert into gtest34p values (1, 7)
+    on conflict (id) do update set a = excluded.c returning *;
+insert into gtest34p values (1, 2)
+    on conflict (id) do update set a = gtest34p.c + excluded.c returning *;
+drop table gtest34p;
