diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc
index 1b1e6c313ad3c557691a6d1695d34aac759f9faf..e85ab80056596a28dc7cca121431b313780f98a0 100644
--- a/src/DynamicModel.cc
+++ b/src/DynamicModel.cc
@@ -3606,6 +3606,8 @@ DynamicModel::addOccbinEquation(expr_t eq, const optional<int>& lineno, map<stri
   auto beq = dynamic_cast<BinaryOpNode*>(eq);
   assert(beq && beq->op_code == BinaryOpcode::equal);
 
+  occbin_regime_trackers[eq_tags.at("name")].addAlternative(regimes_bind, regimes_relax);
+
   // Construct the term to be added to the corresponding equation
   expr_t basic_term = AddMinus(beq->arg1, beq->arg2);
   expr_t term = basic_term;
@@ -4021,3 +4023,144 @@ DynamicModel::checkIsLinear() const
       exit(EXIT_FAILURE);
     }
 }
+
+void
+DynamicModel::checkOccbinRegimes() const
+{
+  for (const auto& [eq_name, tracker] : occbin_regime_trackers)
+    {
+      try
+        {
+          tracker.checkAllAlternativesPresent();
+        }
+      catch (OccbinRegimeTracker::MissingAlternativeException& e)
+        {
+          cerr << "ERROR: for equation '" << eq_name << "', the alternative corresponding to '";
+          if (!e.regimes_bind.empty())
+            {
+              cerr << "bind=";
+              for (bool first_printed {false}; const auto& r : e.regimes_bind)
+                {
+                  if (exchange(first_printed, true))
+                    cerr << ",";
+                  cerr << r;
+                }
+            }
+          if (!e.regimes_bind.empty() && !e.regimes_relax.empty())
+            cerr << "'and '";
+          if (!e.regimes_relax.empty())
+            {
+              cerr << "relax=";
+              for (bool first_printed {false}; const auto& r : e.regimes_relax)
+                {
+                  if (exchange(first_printed, true))
+                    cerr << ",";
+                  cerr << r;
+                }
+            }
+          cerr << "' is not defined" << endl;
+          exit(EXIT_FAILURE);
+        }
+    }
+}
+
+void
+DynamicModel::OccbinRegimeTracker::addAlternative(const vector<string>& regimes_bind,
+                                                  const vector<string>& regimes_relax)
+{
+  // Check that no regime appears in both bind and relax
+  set regimes_bind_sorted(regimes_bind.begin(), regimes_bind.end()),
+      regimes_relax_sorted(regimes_relax.begin(), regimes_relax.end());
+  vector<string> regimes_intersect;
+  ranges::set_intersection(regimes_bind_sorted, regimes_relax_sorted,
+                           back_inserter(regimes_intersect));
+  if (!regimes_intersect.empty())
+    throw RegimeInBothBindAndRelaxException {regimes_intersect.front()};
+
+  // If a regime has never been mentioned before, add it to the list and adapt the alternatives
+  vector<string> regimes_union;
+  ranges::set_union(regimes_bind_sorted, regimes_relax_sorted, back_inserter(regimes_union));
+  for (const auto& r : regimes_union)
+    if (ranges::find(regimes, r) == regimes.end())
+      {
+        regimes.push_back(r);
+        auto alt_copy = alternatives_present;
+        alternatives_present.clear();
+        for (const auto& a : alt_copy)
+          {
+            auto a0 = a, a1 = a;
+            a0.push_back(false);
+            a1.push_back(true);
+            alternatives_present.insert(a0);
+            alternatives_present.insert(a1);
+          }
+      }
+
+  // Create the bit vector(s) corresponding to the function arguments
+  vector<bool> new_alt_template(regimes.size(), false);
+  for (const auto& r : regimes_bind)
+    {
+      int i = distance(regimes.begin(), ranges::find(regimes, r));
+      new_alt_template[i] = true;
+    }
+  set<vector<bool>> new_alts {new_alt_template};
+  set all_regimes_sorted(regimes.begin(), regimes.end());
+  vector<string> regimes_not_mentioned;
+  ranges::set_difference(all_regimes_sorted, regimes_union, back_inserter(regimes_not_mentioned));
+  for (const auto& r : regimes_not_mentioned)
+    {
+      int i = distance(regimes.begin(), ranges::find(regimes, r));
+      auto new_alts_copy = new_alts;
+      for (const auto& a : new_alts_copy)
+        {
+          auto a2 = a;
+          a2[i] = true;
+          new_alts.insert(move(a2));
+        }
+    }
+
+  // Add the new bit vector(s)
+  for (const auto& a : new_alts)
+    {
+      auto [it, success] = alternatives_present.insert(a);
+      if (!success)
+        {
+          auto [regimes_bind_duplicate, regimes_relax_duplicate] = convertBitVectorToRegimes(a);
+          throw AlternativeAlreadyPresentException {regimes_bind_duplicate,
+                                                    regimes_relax_duplicate};
+        }
+    }
+}
+
+void
+DynamicModel::OccbinRegimeTracker::checkAllAlternativesPresent() const
+{
+  vector<bool> a(regimes.size(), false);
+  do
+    {
+      if (!alternatives_present.contains(a))
+        {
+          auto [regimes_bind, regimes_relax] = convertBitVectorToRegimes(a);
+          throw MissingAlternativeException {regimes_bind, regimes_relax};
+        }
+      auto it = ranges::find(a, false);
+      if (it == a.end())
+        break;
+      *it = true;
+      if (it != a.begin())
+        fill(a.begin(), prev(it), false);
+    }
+  while (true);
+}
+
+pair<vector<string>, vector<string>>
+DynamicModel::OccbinRegimeTracker::convertBitVectorToRegimes(const vector<bool>& a) const
+{
+  vector<string> regimes_bind, regimes_relax;
+  for (size_t i = 0; i < regimes.size(); i++)
+    if (a[i])
+      regimes_bind.push_back(regimes[i]);
+    else
+      regimes_relax.push_back(regimes[i]);
+  return {regimes_bind, regimes_relax};
+}
diff --git a/src/DynamicModel.hh b/src/DynamicModel.hh
index 7d77e34419740e70fc147f09d5fdcfbfbd726a0f..fc34f190074d6eb032876d92a476766fd671e33e 100644
--- a/src/DynamicModel.hh
+++ b/src/DynamicModel.hh
@@ -43,6 +43,39 @@ public:
      “model” block. The default should not be too small (see dynare#1389). */
   double balanced_growth_test_tol {1e-6};
 
+  /* For a given equation, tracks all the regimes and the declared alternatives with combinations of
+     bind and relax tags */
+  class OccbinRegimeTracker
+  {
+  private:
+    // The list of regimes used for this equation
+    vector<string> regimes;
+    /* The list of alternatives present for this equation; each alternative is a vector of boolean,
+       of same length as “regimes”; each boolean represents a regime (in the order of “regimes”):
+       false for relax, true for bind */
+    set<vector<bool>> alternatives_present;
+
+  public:
+    struct RegimeInBothBindAndRelaxException
+    {
+      const string regime;
+    };
+    struct AlternativeAlreadyPresentException
+    {
+      const vector<string> regimes_bind, regimes_relax;
+    };
+    void addAlternative(const vector<string>& regimes_bind,
+                        const vector<string>& regimes_relax) noexcept(false);
+    struct MissingAlternativeException
+    {
+      const vector<string> regimes_bind, regimes_relax;
+    };
+    void checkAllAlternativesPresent() const noexcept(false);
+
+  private:
+    pair<vector<string>, vector<string>> convertBitVectorToRegimes(const vector<bool>& a) const;
+  };
+
 private:
   /* Used in the balanced growth test, for skipping equations where the test
      cannot be performed (i.e. when LHS=RHS at the initial values). Should not
@@ -276,6 +309,9 @@ private:
     return blocks_jacob_cols_endo[blk].at({var, lag});
   }
 
+  // Used to check consistency of bind/relax tags; the keys are equation names
+  map<string, OccbinRegimeTracker> occbin_regime_trackers;
+
 protected:
   string
   modelClassName() const override
@@ -695,6 +731,9 @@ public:
   {
     static_mfs = static_mfs_arg;
   }
+
+  // Checks that all alternatives are declared for all Occbin regimes in all equations
+  void checkOccbinRegimes() const;
 };
 
 template<bool julia>
diff --git a/src/ModFile.cc b/src/ModFile.cc
index 3201be71aac621bfb12d1da5498da521f8f7aac3..6ab8b2671de260afcf81958dafbf12254ff5904a 100644
--- a/src/ModFile.cc
+++ b/src/ModFile.cc
@@ -279,6 +279,10 @@ ModFile::checkPass(bool nostrict, bool stochastic)
         }
     }
 
+  /* This check must come before checking that there are as many static-only as dynamic-only
+     equations, see #103 */
+  dynamic_model.checkOccbinRegimes();
+
   if (dynamic_model.staticOnlyEquationsNbr() != dynamic_model.dynamicOnlyEquationsNbr())
     {
       cerr << "ERROR: the number of equations marked [static] must be equal to the number of "
diff --git a/src/ParsingDriver.cc b/src/ParsingDriver.cc
index 94e275454a019658b334cf86e75f0dde620c0ad5..79b68d66df9ba030d151fc79d8c7c07e05f7f30c 100644
--- a/src/ParsingDriver.cc
+++ b/src/ParsingDriver.cc
@@ -2676,8 +2676,44 @@ ParsingDriver::add_model_equal(expr_t arg1, expr_t arg2, map<string, string> eq_
         }
       eq_tags.erase("bind");
       eq_tags.erase("relax");
-      dynamic_model->addOccbinEquation(id, location.begin.line, move(eq_tags), regimes_bind,
-                                       regimes_relax);
+
+      try
+        {
+          dynamic_model->addOccbinEquation(id, location.begin.line, move(eq_tags), regimes_bind,
+                                           regimes_relax);
+        }
+      catch (DynamicModel::OccbinRegimeTracker::RegimeInBothBindAndRelaxException& e)
+        {
+          error("The regime '" + e.regime + "' is both in the 'bind' and 'relax' tags");
+        }
+      catch (DynamicModel::OccbinRegimeTracker::AlternativeAlreadyPresentException& e)
+        {
+          stringstream s;
+          if (!e.regimes_bind.empty())
+            {
+              cerr << "bind=";
+              for (bool first_printed {false}; const auto& r : e.regimes_bind)
+                {
+                  if (exchange(first_printed, true))
+                    cerr << ",";
+                  cerr << r;
+                }
+            }
+          if (!e.regimes_bind.empty() && !e.regimes_relax.empty())
+            cerr << "'and '";
+          if (!e.regimes_relax.empty())
+            {
+              cerr << "relax=";
+              for (bool first_printed {false}; const auto& r : e.regimes_relax)
+                {
+                  if (exchange(first_printed, true))
+                    cerr << ",";
+                  cerr << r;
+                }
+            }
+          error("The alternative corresponding to '" + s.str()
+                + "' has already been declared for this equation");
+        }
     }
   else // General case
     model_tree->addEquation(id, location.begin.line, move(eq_tags));