diff --git a/matlab/compute_moments_varendo.m b/matlab/compute_moments_varendo.m
index e50f1adae8845c2e2dccdd31e15757ea5a6e9227..5d38ce71f979e4d8dc4d6c4842cf4f546324c688 100644
--- a/matlab/compute_moments_varendo.m
+++ b/matlab/compute_moments_varendo.m
@@ -171,7 +171,11 @@ if options_.order==1
             skipline();
         end
         if ~all(diag(M_.H)==0)
-            [observable_name_requested_vars, varlist_pos] = intersect(var_list_, options_.varobs, 'stable');
+            if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+                [observable_name_requested_vars, varlist_pos] = intersect_stable(var_list_, options_.varobs);
+            else
+                [observable_name_requested_vars, varlist_pos] = intersect(var_list_, options_.varobs, 'stable');
+            end
             if ~isempty(observable_name_requested_vars)
                 NumberOfObservedEndogenousVariables = length(observable_name_requested_vars);
                 temp = NaN(NumberOfObservedEndogenousVariables, NumberOfExogenousVariables+1);
diff --git a/matlab/conditional_variance_decomposition.m b/matlab/conditional_variance_decomposition.m
index c9d92e406a4562e8c1fbd937f795fef6aab5b593..3ece5204218e6c153784fdc4eacc1230f5fe27d3 100644
--- a/matlab/conditional_variance_decomposition.m
+++ b/matlab/conditional_variance_decomposition.m
@@ -1,4 +1,5 @@
 function [ConditionalVarianceDecomposition, ConditionalVarianceDecomposition_ME]= conditional_variance_decomposition(M_,options_,dr, Steps, SubsetOfVariables)
+% [ConditionalVarianceDecomposition, ConditionalVarianceDecomposition_ME]= conditional_variance_decomposition(M_,options_,dr, Steps, SubsetOfVariables)
 % This function computes the conditional variance decomposition of a given state space model
 % for a subset of endogenous variables.
 %
@@ -88,7 +89,11 @@ end
 % Measurement error
 
 if ~all(diag(M_.H)==0)
-    [observable_pos,index_subset,index_observables]=intersect(SubsetOfVariables,options_.varobs_id,'stable');
+    if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+        [observable_pos,index_subset,index_observables]=intersect_stable(SubsetOfVariables,options_.varobs_id);
+    else
+        [observable_pos,index_subset,index_observables]=intersect(SubsetOfVariables,options_.varobs_id,'stable');
+    end
     ME_Variance=diag(M_.H);
 
     ConditionalVarianceDecomposition_ME = zeros(length(observable_pos),length(Steps),number_of_state_innovations+1);
diff --git a/matlab/conditional_variance_decomposition_ME_mc_analysis.m b/matlab/conditional_variance_decomposition_ME_mc_analysis.m
index 52e3ff344e1c0bafe24dd790cc7d6e30a2acc207..c4d4e349694592eaded997db30f36fa436a72ca1 100644
--- a/matlab/conditional_variance_decomposition_ME_mc_analysis.m
+++ b/matlab/conditional_variance_decomposition_ME_mc_analysis.m
@@ -1,5 +1,6 @@
 function oo_ = ...
     conditional_variance_decomposition_ME_mc_analysis(NumberOfSimulations, type, dname, fname, Steps, exonames, exo, var_list, endo, mh_conf_sig, oo_,options_)
+%oo_ = conditional_variance_decomposition_ME_mc_analysis(NumberOfSimulations, type, dname, fname, Steps, exonames, exo, var_list, endo, mh_conf_sig, oo_,options_)
 % This function analyses the (posterior or prior) distribution of the
 % endogenous variables' conditional variance decomposition with measurement error.
 %
@@ -22,7 +23,7 @@ function oo_ = ...
 % OUTPUTS
 %   oo_          [structure]        Dynare structure where the results are saved.
 
-% Copyright © 2017-2020 Dynare Team
+% Copyright © 2017-2023 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -63,7 +64,12 @@ if isempty(exogenous_variable_index)
     end
 end
 
-[observable_pos_requested_vars,index_subset,index_observables]=intersect(var_list,options_.varobs,'stable');
+if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+    [~,index_subset]=intersect_stable(var_list,options_.varobs);
+else
+    [~,index_subset]=intersect(var_list,options_.varobs,'stable');
+end
+
 matrix_pos=strmatch(endo, var_list(index_subset),'exact');
 name_1 = endo;
 name_2 = exo;
diff --git a/matlab/disp_moments.m b/matlab/disp_moments.m
index 0dd5953269a402d4650b0c851188c4e1c4c18579..b9759cd51dc43ca9e4975de0295b644d02474af9 100644
--- a/matlab/disp_moments.m
+++ b/matlab/disp_moments.m
@@ -11,7 +11,7 @@ function oo_=disp_moments(y,var_list,M_,options_,oo_)
 % OUTPUTS
 %   oo_                 [structure]    Dynare's results structure,
 
-% Copyright © 2001-2021 Dynare Team
+% Copyright © 2001-2023 Dynare Team
 %
 % This file is part of Dynare.
 %
@@ -50,7 +50,11 @@ y = y(ivar,options_.drop+1:end)';
 
 ME_present=0;
 if ~all(M_.H==0)
-    [observable_pos_requested_vars, index_subset, index_observables] = intersect(ivar, options_.varobs_id, 'stable');
+    if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+        [observable_pos_requested_vars, index_subset, index_observables] = intersect_stable(ivar, options_.varobs_id);
+    else
+        [observable_pos_requested_vars, index_subset, index_observables] = intersect(ivar, options_.varobs_id, 'stable');
+    end
     if ~isempty(observable_pos_requested_vars)
         ME_present=1;
         i_ME = setdiff([1:size(M_.H,1)],find(diag(M_.H) == 0)); % find ME with 0 variance
diff --git a/matlab/dynare_config.m b/matlab/dynare_config.m
index 31e7dce80b30eb2b97cdadf94c7bd7e37d9bd7c6..2dd275572c7441adb13c1d1aa2e6bb654c9e6c60 100644
--- a/matlab/dynare_config.m
+++ b/matlab/dynare_config.m
@@ -122,6 +122,11 @@ if isoctave || matlab_ver_less_than('8.4')
     p{end+1} = '/missing/datetime';
 end
 
+% intersect with 'stable' flag is broken before Octave 8.4, bug #60347
+if isoctave && octave_ver_less_than('8.4')
+    p{end+1} = '/missing/intersect_stable';
+end
+
 P = cellfun(@(c)[dynareroot(1:end-1) c], p, 'uni',false);
 
 % Get mex files folder(s)
diff --git a/matlab/missing/intersect_stable/intersect_stable.m b/matlab/missing/intersect_stable/intersect_stable.m
new file mode 100644
index 0000000000000000000000000000000000000000..155e1540103330ebda201b3bd18e85439dc753d1
--- /dev/null
+++ b/matlab/missing/intersect_stable/intersect_stable.m
@@ -0,0 +1,91 @@
+function [c, ia, ib] = intersect_stable(a, b)
+% Crude implementation of intersect(…, 'stable'), which is missing before
+% Octave 6.2 and buggy before 8.4 (#60347)
+
+% Copyright (C) 2019-2023 Dynare Team
+%
+% This file is part of Dynare.
+%
+% Dynare is free software: you can redistribute it and/or modify
+% it under the terms of the GNU General Public License as published by
+% the Free Software Foundation, either version 3 of the License, or
+% (at your option) any later version.
+%
+% Dynare is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+% GNU General Public License for more details.
+%
+% You should have received a copy of the GNU General Public License
+% along with Dynare.  If not, see <http://www.gnu.org/licenses/>.
+
+if nargin ~= 2
+    error('intersect_stable: needs exactly 2 input arguments');
+end
+
+if isnumeric (a) && isnumeric (b)
+    c = [];
+elseif iscell (b)
+    c = {};
+else
+    c = '';
+end
+ia = [];
+ib = [];
+
+if isempty (a) || isempty (b)
+    return
+else
+    isrowvec = isrow (a) && isrow (b);
+
+    for i = 1:numel(a)
+        if iscellstr(c)
+            idx = strcmp(a(i), b);
+        else
+            idx = a(i) == b;
+        end
+        if any(idx) && ~ismember(a(i), c)
+            c = [c(:); a(i)];
+            if nargout > 1
+                ia = [ia, i];
+                ib = [ib, find(idx)];
+            end
+        end
+    end
+
+    %% Adjust output orientation for MATLAB compatibility
+    if isrowvec
+        c = c.';
+    end
+end
+end
+
+%!test
+%! a = [3 4 1 5];
+%! b = [2 4 9 1 6];
+%! [c,ia,ib]=intersect_stable(a,b);
+%! assert(c, [4 1])
+%! assert(ia, [2 3])
+%! assert(ib, [2 4])
+%! assert(a(ia), c)
+%! assert(b(ib), c)
+
+%!test
+%! a = [3 4 1 5]';
+%! b = [2 4 9 1 6]';
+%! [c,ia,ib]=intersect_stable(a,b);
+%! assert(c, [4 1]')
+%! assert(ia, [2 3])
+%! assert(ib, [2 4])
+%! assert(a(ia), c)
+%! assert(b(ib), c)
+
+%!test
+%! a = { 'defun', 'mapcar', 'let', 'eval-when'};
+%! b = { 'setf', 'let', 'list', 'cdr', 'defun'};
+%! [c,ia,ib]=intersect_stable(a,b);
+%! assert(c, { 'defun', 'let' })
+%! assert(ia, [1 3])
+%! assert(ib, [5 2])
+%! assert(a(ia), c)
+%! assert(b(ib), c)
diff --git a/matlab/moments/check_measurement_error_requested_vars.m b/matlab/moments/check_measurement_error_requested_vars.m
index 76bee009d90b1b135418bd15a58f90bea7320729..6f7419b1fd47a94f044f14d4732f7a2c08540e9e 100644
--- a/matlab/moments/check_measurement_error_requested_vars.m
+++ b/matlab/moments/check_measurement_error_requested_vars.m
@@ -38,7 +38,11 @@ observable_pos_requested_vars=[];
 
 ME_present=false;
 if ~all(diag(M_.H)==0)
-    [observable_pos_requested_vars,index_subset,index_observables]=intersect(ivar,options_.varobs_id,'stable');
+    if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+        [observable_pos_requested_vars,index_subset,index_observables]=intersect_stable(ivar,options_.varobs_id);
+    else
+        [observable_pos_requested_vars,index_subset,index_observables]=intersect(ivar,options_.varobs_id,'stable');
+    end
     if ~isempty(observable_pos_requested_vars)
         ME_present=true;
     end
diff --git a/matlab/moments/display_unconditional_variance_decomposition.m b/matlab/moments/display_unconditional_variance_decomposition.m
index df547d02cf12d4083605c385c3905d20f8ca921a..f07ecb91b672d3fb788da1bef51530ba1c79574a 100644
--- a/matlab/moments/display_unconditional_variance_decomposition.m
+++ b/matlab/moments/display_unconditional_variance_decomposition.m
@@ -45,7 +45,11 @@ if M_.exo_nbr > 1
     lh = cellofchararraymaxlength(labels)+2;
     dyntable(options_, title, headers, labels, 100*oo_.gamma_y{options_.ar+2}(stationary_vars,:), lh, 8, 2);
     if ME_present
-        [stationary_observables, pos_index_subset] = intersect(index_subset, stationary_vars, 'stable');
+        if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+            [stationary_observables, pos_index_subset] = intersect_stable(index_subset, stationary_vars);
+        else
+            [stationary_observables, pos_index_subset] = intersect(index_subset, stationary_vars, 'stable');
+        end
         headers_ME = vertcat(headers, 'ME');
         labels=get_labels_transformed_vars(M_.endo_names,ivar(stationary_observables),options_,false);
         dyntable(options_, [title,' WITH MEASUREMENT ERROR'], headers_ME, labels, ...
diff --git a/matlab/posterior_analysis.m b/matlab/posterior_analysis.m
index 3b523c0a7c392bda8dbfacea7d3cb63c13bd9ff9..2bc14e27c8f1d5ad0243bc38fa0a53c4396b0f0b 100644
--- a/matlab/posterior_analysis.m
+++ b/matlab/posterior_analysis.m
@@ -66,7 +66,11 @@ switch type
                                              M_.exo_names,arg2,vartan,arg1,options_.mh_conf_sig,oo_,options_);
     if ~all(diag(M_.H)==0)
         if strmatch(arg1,options_.varobs,'exact')
-            [observable_name_requested_vars,index_subset,index_observables]=intersect(vartan,options_.varobs,'stable');
+            if isoctave && octave_ver_less_than('8.4') %Octave bug #60347
+                observable_name_requested_vars=intersect_stable(vartan,options_.varobs);
+            else
+                observable_name_requested_vars=intersect(vartan,options_.varobs,'stable');
+            end
             oo_ = variance_decomposition_ME_mc_analysis(SampleSize,'posterior',M_.dname,M_.fname,...
                                                         [M_.exo_names;'ME'],arg2,observable_name_requested_vars,arg1,options_.mh_conf_sig,oo_,options_);
         end