diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index 32e5f32881f5ed7339cca084e96e752a079be4d4..e1401abfeaf8e3bcaed32df9e26f6ea6bedc3912 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -2453,7 +2453,10 @@ DynamicModel::writeDynamicFile(const string &basename, bool use_dll, const strin
     writeDynamicMFile(basename);
   // The legacy representation is no longer produced for Julia
 
-  writeDynamicBytecode(basename);
+  /* Discretionary optimal policy models don’t have as many variables as
+     equations; bytecode does not support that case */
+  if (static_cast<int>(equations.size()) == symbol_table.endo_nbr())
+    writeDynamicBytecode(basename);
   if (block_decomposed)
     writeDynamicBlockBytecode(basename);
 
diff --git a/src/ModelEquationBlock.cc b/src/ModelEquationBlock.cc
index 120847977cf35b7ff363dea66ae0494d0b65e386..d2a0c1d804a1044a801882a9cd9ea508b016293a 100644
--- a/src/ModelEquationBlock.cc
+++ b/src/ModelEquationBlock.cc
@@ -37,12 +37,6 @@ PlannerObjective::computingPassBlock([[maybe_unused]] const eval_context_t &eval
   // Disable block decomposition on planner objective
 }
 
-void
-PlannerObjective::writeStaticBytecode([[maybe_unused]] const string &basename) const
-{
-  // Disable bytecode output, because there are not as many variables as equations
-}
-
 OrigRamseyDynamicModel::OrigRamseyDynamicModel(SymbolTable &symbol_table_arg,
                                                NumericalConstants &num_constants_arg,
                                                ExternalFunctionsTable &external_functions_table_arg,
diff --git a/src/ModelEquationBlock.hh b/src/ModelEquationBlock.hh
index 9f8536d436e307997dd2044191d3f1af134f316c..8efe6aaf7aa24f98132b7e7afe7416bd04ece960 100644
--- a/src/ModelEquationBlock.hh
+++ b/src/ModelEquationBlock.hh
@@ -41,7 +41,6 @@ protected:
 
 private:
   void computingPassBlock(const eval_context_t &eval_context, bool no_tmp_terms) override;
-  void writeStaticBytecode(const string &basename) const override;
 };
 
 class OrigRamseyDynamicModel : public DynamicModel
diff --git a/src/StaticModel.cc b/src/StaticModel.cc
index 6760a4925cb6742956e6997cf16fb5ca4d763f23..35076bb01773f08cadfe92bc3d9ddada0b364ea1 100644
--- a/src/StaticModel.cc
+++ b/src/StaticModel.cc
@@ -475,7 +475,11 @@ StaticModel::writeStaticFile(const string &basename, bool use_dll, const string
     writeStaticMFile(basename);
   // The legacy representation is no longer produced for Julia
 
-  writeStaticBytecode(basename);
+  /* PlannerObjective subclass or discretionary optimal policy models don’t
+     have as many variables as equations; bytecode does not support that
+     case */
+  if (static_cast<int>(equations.size()) == symbol_table.endo_nbr())
+    writeStaticBytecode(basename);
   if (block_decomposed)
     writeStaticBlockBytecode(basename);
 
diff --git a/src/StaticModel.hh b/src/StaticModel.hh
index 8df4d084abd3569ac13f1025f30a9234167c9e71..5e98f28f0db2b67e6b901ef19dd62df7d142e049 100644
--- a/src/StaticModel.hh
+++ b/src/StaticModel.hh
@@ -60,7 +60,7 @@ private:
   void writeStaticBlockBytecode(const string &basename) const;
 
   //! Writes the code of the model in virtual machine bytecode
-  virtual void writeStaticBytecode(const string &basename) const;
+  void writeStaticBytecode(const string &basename) const;
 
   //! Computes jacobian and prepares for equation normalization
   /*! Using values from initval/endval blocks and parameter initializations: