diff --git a/arbor/arbexcept.cpp b/arbor/arbexcept.cpp
index 76dfdd580e2328f128832ba45aaf145b1afe784d..f03cfae162257379d7b0b3eedeb456d768b30c50 100644
--- a/arbor/arbexcept.cpp
+++ b/arbor/arbexcept.cpp
@@ -15,6 +15,35 @@ bad_cell_description::bad_cell_description(cell_kind kind, cell_gid_type gid):
     gid(gid),
     kind(kind)
 {}
+bad_target_description::bad_target_description(cell_gid_type gid, cell_size_type rec_val, cell_size_type cell_val):
+    arbor_exception(pprintf("Model building error on cell {}: recipe::num_targets(gid={}) = {} is greater than the number of synapses on the cell = {}", gid, gid, rec_val, cell_val)),
+    gid(gid), rec_val(rec_val), cell_val(cell_val)
+{}
+
+bad_source_description::bad_source_description(cell_gid_type gid, cell_size_type rec_val, cell_size_type cell_val):
+    arbor_exception(pprintf("Model building error on cell {}: recipe::num_sources(gid={}) = {} is greater than the number of detectors on the cell = {}", gid, gid, rec_val, cell_val)),
+    gid(gid), rec_val(rec_val), cell_val(cell_val)
+{}
+
+bad_connection_source_gid::bad_connection_source_gid(cell_gid_type gid, cell_gid_type src_gid, cell_size_type num_cells):
+    arbor_exception(pprintf("Model building error on cell {}: connection source gid {} is out of range: there are only {} cells in the model, in the range [{}:{}].", gid, src_gid, num_cells, 0, num_cells-1)),
+    gid(gid), src_gid(src_gid), num_cells(num_cells)
+{}
+
+bad_connection_source_lid::bad_connection_source_lid(cell_gid_type gid, cell_lid_type src_lid, cell_size_type num_sources):
+    arbor_exception(pprintf("Model building error on cell {}: connection source index {} is out of range. Cell {} has {} sources, in the range [{}:{}].", gid, src_lid, gid, num_sources, 0, num_sources-1)),
+    gid(gid), src_lid(src_lid), num_sources(num_sources)
+{}
+
+bad_connection_target_gid::bad_connection_target_gid(cell_gid_type gid, cell_gid_type tgt_gid):
+    arbor_exception(pprintf("Model building error on cell {}: connection target gid {} has to match cell gid {}].", gid, tgt_gid, gid)),
+    gid(gid), tgt_gid(tgt_gid)
+{}
+
+bad_connection_target_lid::bad_connection_target_lid(cell_gid_type gid, cell_lid_type tgt_lid, cell_size_type num_targets):
+    arbor_exception(pprintf("Model building error on cell {}: connection target index {} is out of range. Cell {} has {} targets, in the range [{}:{}].", gid, tgt_lid, gid, num_targets, 0, num_targets-1)),
+    gid(gid), tgt_lid(tgt_lid), num_targets(num_targets)
+{}
 
 bad_global_property::bad_global_property(cell_kind kind):
     arbor_exception(pprintf("bad global property for cell kind {}", kind)),
@@ -32,6 +61,19 @@ gj_unsupported_domain_decomposition::gj_unsupported_domain_decomposition(cell_gi
     gid_1(gid_1)
 {}
 
+bad_gj_connection_gid::bad_gj_connection_gid(cell_gid_type gid, cell_gid_type site_0, cell_gid_type site_1):
+    arbor_exception(pprintf("Model building error on cell {}: recipe::gap_junctions_on(gid={}) -> cell {} <-> cell{}: one of the sites must be on the cell with gid = {})", gid, gid, site_0, site_1, gid)),
+    gid(gid),
+    site_0(site_0),
+    site_1(site_1)
+{}
+
+bad_gj_connection_lid::bad_gj_connection_lid(cell_gid_type gid, cell_member_type site):
+    arbor_exception(pprintf("Model building error on cell {}: gap junction index {} on cell {} does not exist)", gid, site.gid, site.index)),
+    gid(gid),
+    site(site)
+{}
+
 gj_kind_mismatch::gj_kind_mismatch(cell_gid_type gid_0, cell_gid_type gid_1):
     arbor_exception(pprintf("Cells on gid {} and {} connected via gap junction have different cell kinds", gid_0, gid_1)),
     gid_0(gid_0),
diff --git a/arbor/communication/communicator.cpp b/arbor/communication/communicator.cpp
index 50acd362c085bf3237c5a0e710fa028bbb96521b..638ad093f4f6dc1b25597e6333ee2c1e9560dca6 100644
--- a/arbor/communication/communicator.cpp
+++ b/arbor/communication/communicator.cpp
@@ -6,6 +6,7 @@
 #include <arbor/domain_decomposition.hpp>
 #include <arbor/recipe.hpp>
 #include <arbor/spike.hpp>
+#include <include/arbor/arbexcept.hpp>
 
 #include "algorithms.hpp"
 #include "communication/gathered_vector.hpp"
@@ -32,6 +33,7 @@ communicator::communicator(const recipe& rec,
     num_domains_ = distributed_->size();
     num_local_groups_ = dom_dec.groups.size();
     num_local_cells_ = dom_dec.num_local_cells;
+    auto num_total_cells = rec.num_cells();
 
     // For caching information about each cell
     struct gid_info {
@@ -75,9 +77,24 @@ communicator::communicator(const recipe& rec,
     std::vector<unsigned> src_domains;
     src_domains.reserve(n_cons);
     std::vector<cell_size_type> src_counts(num_domains_);
-    for (const auto& g: gid_infos) {
-        for (auto con: g.conns) {
-            const auto src = dom_dec.gid_domain(con.source.gid);
+
+    for (const auto& cell: gid_infos) {
+        auto num_targets = rec.num_targets(cell.gid);
+        for (auto c: cell.conns) {
+            auto num_sources = rec.num_sources(c.source.gid);
+            if (c.source.gid >= num_total_cells) {
+                throw arb::bad_connection_source_gid(cell.gid, c.source.gid, num_total_cells);
+            }
+            if (c.source.index >= num_sources) {
+                throw arb::bad_connection_source_lid(cell.gid, c.source.index, num_sources);
+            }
+            if (c.dest.gid != cell.gid) {
+                throw arb::bad_connection_target_gid(cell.gid, c.dest.gid);
+            }
+            if (c.dest.index >= num_targets) {
+                throw arb::bad_connection_target_lid(cell.gid, c.dest.index, num_targets);
+            }
+            const auto src = dom_dec.gid_domain(c.source.gid);
             src_domains.push_back(src);
             src_counts[src]++;
         }
diff --git a/arbor/fvm_lowered_cell_impl.hpp b/arbor/fvm_lowered_cell_impl.hpp
index 26e3a6458db352f499fe6e57d0893cea782ee007..3851ef02835955532520dc3fe39a4297d40ef216 100644
--- a/arbor/fvm_lowered_cell_impl.hpp
+++ b/arbor/fvm_lowered_cell_impl.hpp
@@ -526,7 +526,6 @@ void fvm_lowered_cell_impl<Backend>::initialize(
         }
     }
 
-    // Collect detectors, probe handles.
 
     std::vector<index_type> detector_cv;
     std::vector<value_type> detector_threshold;
@@ -535,6 +534,17 @@ void fvm_lowered_cell_impl<Backend>::initialize(
     for (auto cell_idx: make_span(ncell)) {
         cell_gid_type gid = gids[cell_idx];
 
+        // Sanity check recipe
+        auto& cell = cells[cell_idx];
+        if (rec.num_sources(gid) > cell.detectors().size()) {
+            throw arb::bad_source_description(gid, rec.num_sources(gid), cell.detectors().size());;
+        }
+        auto cell_targets = util::sum_by(cell.synapses(), [](auto& syn) {return syn.second.size();});
+        if (cell_targets > rec.num_targets(gid)) {
+            throw arb::bad_target_description(gid, rec.num_targets(gid), cell_targets);
+        }
+
+        // Collect detectors, probe handles.
         for (auto entry: cells[cell_idx].detectors()) {
             detector_cv.push_back(D.geometry.location_cv(cell_idx, entry.loc, cv_prefer::cv_empty));
             detector_threshold.push_back(entry.item.threshold);
@@ -588,16 +598,16 @@ std::vector<fvm_gap_junction> fvm_lowered_cell_impl<Backend>::fvm_gap_junctions(
         auto gj_list = rec.gap_junctions_on(gid);
         for (auto g: gj_list) {
             if (gid != g.local.gid && gid != g.peer.gid) {
-                throw arb::bad_cell_description(cell_kind::cable, gid);
+                throw arb::bad_gj_connection_gid(gid, g.local.gid, g.peer.gid);
             }
-            cell_gid_type cv0, cv1;
-            try {
-                cv0 = gid_to_cvs[g.local.gid].at(g.local.index);
-                cv1 = gid_to_cvs[g.peer.gid].at(g.peer.index);
+            if (g.local.index >= gid_to_cvs[g.local.gid].size()) {
+                throw arb::bad_gj_connection_lid(gid, g.local);
             }
-            catch (std::out_of_range&) {
-                throw arb::bad_cell_description(cell_kind::cable, gid);
+            if (g.peer.index >= gid_to_cvs[g.peer.gid].size()) {
+                throw arb::bad_gj_connection_lid(gid, g.peer);
             }
+            auto cv0 = gid_to_cvs[g.local.gid][g.local.index];
+            auto cv1 = gid_to_cvs[g.peer.gid][g.peer.index];
             if (gid != g.local.gid) {
                 std::swap(cv0, cv1);
             }
diff --git a/arbor/include/arbor/arbexcept.hpp b/arbor/include/arbor/arbexcept.hpp
index 6eb4a4bfd8016ae65d3a48db1a7f361f02592e45..e1dcf8b1c4338a38b92705a447e69bdcccad10e7 100644
--- a/arbor/include/arbor/arbexcept.hpp
+++ b/arbor/include/arbor/arbexcept.hpp
@@ -35,6 +35,43 @@ struct bad_cell_description: arbor_exception {
     cell_kind kind;
 };
 
+struct bad_target_description: arbor_exception {
+    bad_target_description(cell_gid_type gid, cell_size_type rec_val, cell_size_type cell_val);
+    cell_gid_type gid;
+    cell_size_type rec_val, cell_val;
+};
+
+struct bad_source_description: arbor_exception {
+    bad_source_description(cell_gid_type gid, cell_size_type rec_val, cell_size_type cell_val);
+    cell_gid_type gid;
+    cell_size_type rec_val, cell_val;
+};
+
+struct bad_connection_source_gid: arbor_exception {
+    bad_connection_source_gid(cell_gid_type gid, cell_gid_type src_gid, cell_size_type num_cells);
+    cell_gid_type gid, src_gid;
+    cell_size_type num_cells;
+};
+
+struct bad_connection_source_lid: arbor_exception {
+    bad_connection_source_lid(cell_gid_type gid, cell_lid_type src_lid, cell_size_type num_sources);
+    cell_gid_type gid;
+    cell_lid_type src_lid;
+    cell_size_type num_sources;
+};
+
+struct bad_connection_target_gid: arbor_exception {
+    bad_connection_target_gid(cell_gid_type gid, cell_gid_type tgt_gid);
+    cell_gid_type gid, tgt_gid;
+};
+
+struct bad_connection_target_lid: arbor_exception {
+    bad_connection_target_lid(cell_gid_type gid, cell_lid_type tgt_lid, cell_size_type num_targets);
+    cell_gid_type gid;
+    cell_lid_type tgt_lid;
+    cell_size_type num_targets;
+};
+
 struct bad_global_property: arbor_exception {
     explicit bad_global_property(cell_kind kind);
     cell_kind kind;
@@ -50,6 +87,17 @@ struct gj_kind_mismatch: arbor_exception {
     cell_gid_type gid_0, gid_1;
 };
 
+struct bad_gj_connection_gid: arbor_exception {
+    bad_gj_connection_gid(cell_gid_type gid, cell_gid_type site_0, cell_gid_type site_1);
+    cell_gid_type gid, site_0, site_1;
+};
+
+struct bad_gj_connection_lid: arbor_exception {
+    bad_gj_connection_lid(cell_gid_type gid, cell_member_type site);
+    cell_gid_type gid;
+    cell_member_type site;
+};
+
 // Domain decomposition errors:
 
 struct gj_unsupported_domain_decomposition: arbor_exception {
diff --git a/arbor/partition_load_balance.cpp b/arbor/partition_load_balance.cpp
index 5236658e2ba27cb3bd098ff534344af0ddc69b6f..e8fd413557355138f01db42c29b9d7e51f1b2644 100644
--- a/arbor/partition_load_balance.cpp
+++ b/arbor/partition_load_balance.cpp
@@ -93,7 +93,7 @@ domain_decomposition partition_load_balance(
                     auto conns = rec.gap_junctions_on(element);
                     for (auto c: conns) {
                         if (element != c.local.gid && element != c.peer.gid) {
-                            throw bad_cell_description(cell_kind::cable, element);
+                            throw bad_gj_connection_gid(element, c.local.gid, c.peer.gid);
                         }
                         cell_member_type other = c.local.gid == element ? c.peer : c.local;
 
diff --git a/test/simple_recipes.hpp b/test/simple_recipes.hpp
index 9a1c1dad7d324d8792b8cc78935ca3881043b7ce..5344a1361bde35598945793894f24c37b15c4a5a 100644
--- a/test/simple_recipes.hpp
+++ b/test/simple_recipes.hpp
@@ -12,6 +12,8 @@
 #include <arbor/recipe.hpp>
 #include <arbor/util/unique_any.hpp>
 
+#include "util/rangeutil.hpp"
+
 namespace arb {
 
 // Common functionality: maintain an unordered map of probe data
@@ -119,7 +121,7 @@ public:
     }
 
     cell_size_type num_targets(cell_gid_type i) const override {
-        return cells_.at(i).synapses().size();
+        return util::sum_by(cells_.at(i).synapses(), [](auto& syn) {return syn.second.size();});
     }
 
     util::unique_any get_cell_description(cell_gid_type i) const override {
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index eb3ef8d6476ae54cc0b3bcd512c037bb1502dd72..ba08e6c53ae8a867058b594f8c1f3da8aaa0b5d7 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -138,6 +138,7 @@ set(unit_sources
     test_pp_util.cpp
     test_probe.cpp
     test_range.cpp
+    test_recipe.cpp
     test_ratelem.cpp
     test_schedule.cpp
     test_scope_exit.cpp
diff --git a/test/unit/test_recipe.cpp b/test/unit/test_recipe.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..958b66e967e0aa77bcfadf886ea3c027c05f3da1
--- /dev/null
+++ b/test/unit/test_recipe.cpp
@@ -0,0 +1,293 @@
+#include "../gtest.h"
+
+#include <arbor/common_types.hpp>
+#include <arbor/domain_decomposition.hpp>
+#include <arbor/load_balance.hpp>
+#include <arbor/cable_cell.hpp>
+#include <arbor/recipe.hpp>
+#include <arbor/simulation.hpp>
+
+#include <arborenv/concurrency.hpp>
+
+#include "util/span.hpp"
+
+#include "../common_cells.hpp"
+
+using namespace arb;
+using arb::util::make_span;
+
+namespace {
+    class custom_recipe: public recipe {
+    public:
+        custom_recipe(std::vector<cable_cell> cells,
+                      std::vector<cell_size_type> num_sources,
+                      std::vector<cell_size_type> num_targets,
+                      std::vector<std::vector<cell_connection>> conns,
+                      std::vector<std::vector<gap_junction_connection>> gjs):
+            num_cells_(cells.size()),
+            num_sources_(num_sources),
+            num_targets_(num_targets),
+            connections_(conns),
+            gap_junctions_(gjs),
+            cells_(cells) {}
+
+        cell_size_type num_cells() const override {
+            return num_cells_;
+        }
+        arb::util::unique_any get_cell_description(cell_gid_type gid) const override {
+            return cells_[gid];
+        }
+        cell_kind get_cell_kind(cell_gid_type gid) const override {
+            return cell_kind::cable;
+        }
+        std::vector<gap_junction_connection> gap_junctions_on(cell_gid_type gid) const override {
+            return gap_junctions_[gid];
+        }
+        std::vector<cell_connection> connections_on(cell_gid_type gid) const override {
+            return connections_[gid];
+        }
+        cell_size_type num_sources(cell_gid_type gid) const override {
+            return num_sources_[gid];
+        }
+        cell_size_type num_targets(cell_gid_type gid) const override {
+            return num_targets_[gid];
+        }
+        arb::util::any get_global_properties(cell_kind) const override {
+            arb::cable_cell_global_properties a;
+            a.default_parameters = arb::neuron_parameter_defaults;
+            return a;
+        }
+
+    private:
+        cell_size_type num_cells_;
+        std::vector<cell_size_type> num_sources_, num_targets_;
+        std::vector<std::vector<cell_connection>> connections_;
+        std::vector<std::vector<gap_junction_connection>> gap_junctions_;
+        std::vector<cable_cell> cells_;
+    };
+
+    cable_cell custom_cell(cell_size_type num_detectors, cell_size_type num_synapses, cell_size_type num_gj) {
+        arb::segment_tree tree;
+        tree.append(arb::mnpos, {0,0,0,10}, {0,0,20,10}, 1); // soma
+        tree.append(0, {0,0, 20, 2}, {0,0, 320, 2}, 3);  // dendrite
+
+        arb::cable_cell cell(tree, {});
+
+        // Add a num_detectors detectors to the cell.
+        for (auto i: util::make_span(num_detectors)) {
+            cell.place(arb::mlocation{0,(double)i/num_detectors}, arb::threshold_detector{10});
+        }
+
+        // Add a num_synapses synapses to the cell.
+        for (auto i: util::make_span(num_synapses)) {
+            cell.place(arb::mlocation{0,(double)i/num_synapses}, "expsyn");
+        }
+
+        // Add a num_gj gap_junctions to the cell.
+        for (auto i: util::make_span(num_gj)) {
+            cell.place(arb::mlocation{0,(double)i/num_gj}, arb::gap_junction_site{});
+        }
+
+        return cell;
+    }
+}
+
+// test assumes one domain
+TEST(recipe, num_sources)
+{
+    arb::proc_allocation resources;
+    if (auto nt = arbenv::get_env_num_threads()) {
+        resources.num_threads = nt;
+    }
+    else {
+        resources.num_threads = arbenv::thread_concurrency();
+    }
+    auto context = make_context(resources);
+    auto cell = custom_cell(1, 0, 0);
+
+    {
+        auto recipe_0 = custom_recipe({cell}, {1}, {0}, {{}}, {{}});
+        auto decomp_0 = partition_load_balance(recipe_0, context);
+
+        EXPECT_NO_THROW(simulation(recipe_0, decomp_0, context));
+    }
+    {
+        auto recipe_1 = custom_recipe({cell}, {2}, {0}, {{}}, {{}});
+        auto decomp_1 = partition_load_balance(recipe_1, context);
+
+        EXPECT_THROW(simulation(recipe_1, decomp_1, context), arb::bad_source_description);
+    }
+}
+
+TEST(recipe, num_targets)
+{
+    arb::proc_allocation resources;
+    if (auto nt = arbenv::get_env_num_threads()) {
+        resources.num_threads = nt;
+    }
+    else {
+        resources.num_threads = arbenv::thread_concurrency();
+    }
+    auto context = make_context(resources);
+    auto cell = custom_cell(0, 2, 0);
+
+    {
+        auto recipe_0 = custom_recipe({cell}, {0}, {2}, {{}}, {{}});
+        auto decomp_0 = partition_load_balance(recipe_0, context);
+
+        EXPECT_NO_THROW(simulation(recipe_0, decomp_0, context));
+    }
+    {
+        auto recipe_1 = custom_recipe({cell}, {0}, {1}, {{}}, {{}});
+        auto decomp_1 = partition_load_balance(recipe_1, context);
+
+        EXPECT_THROW(simulation(recipe_1, decomp_1, context), arb::bad_target_description);
+    }
+}
+
+TEST(recipe, gap_junctions)
+{
+    arb::proc_allocation resources;
+    if (auto nt = arbenv::get_env_num_threads()) {
+        resources.num_threads = nt;
+    }
+    else {
+        resources.num_threads = arbenv::thread_concurrency();
+    }
+    auto context = make_context(resources);
+
+    auto cell_0 = custom_cell(0, 0, 3);
+    auto cell_1 = custom_cell(0, 0, 3);
+
+    {
+        std::vector<arb::gap_junction_connection> gjs_0 = {{{0, 0}, {1, 1}, 0.1},
+                                                           {{0, 1}, {1, 2}, 0.1},
+                                                           {{0, 2}, {1, 0}, 0.1}};
+
+        auto recipe_0 = custom_recipe({cell_0, cell_1}, {0, 0}, {0, 0}, {{}, {}}, {gjs_0, gjs_0});
+        auto decomp_0 = partition_load_balance(recipe_0, context);
+
+        EXPECT_NO_THROW(simulation(recipe_0, decomp_0, context));
+    }
+    {
+        std::vector<arb::gap_junction_connection> gjs_1 = {{{0, 0}, {1, 1}, 0.1},
+                                                           {{0, 1}, {1, 2}, 0.1},
+                                                           {{0, 2}, {1, 5}, 0.1}};
+
+        auto recipe_1 = custom_recipe({cell_0, cell_1}, {0, 0}, {0, 0}, {{}, {}}, {gjs_1, gjs_1});
+        auto decomp_1 = partition_load_balance(recipe_1, context);
+
+        EXPECT_THROW(simulation(recipe_1, decomp_1, context), arb::bad_gj_connection_lid);
+
+    }
+    {
+        std::vector<arb::gap_junction_connection> gjs_2 = {{{0, 0}, {1, 1}, 0.1},
+                                                           {{0, 1}, {1, 2}, 0.1},
+                                                           {{0, 2}, {3, 0}, 0.1}};
+
+        auto recipe_2 = custom_recipe({cell_0, cell_1}, {0, 0}, {0, 0}, {{}, {}}, {gjs_2, gjs_2});
+        auto context = make_context(resources);
+
+        EXPECT_THROW(partition_load_balance(recipe_2, context), arb::bad_gj_connection_gid);
+    }
+}
+
+TEST(recipe, connections)
+{
+    arb::proc_allocation resources;
+    if (auto nt = arbenv::get_env_num_threads()) {
+        resources.num_threads = nt;
+    }
+    else {
+        resources.num_threads = arbenv::thread_concurrency();
+    }
+    auto context = make_context(resources);
+
+    auto cell_0 = custom_cell(1, 2, 0);
+    auto cell_1 = custom_cell(2, 1, 0);
+    std::vector<arb::cell_connection> conns_0, conns_1;
+    {
+        conns_0 = {{{1, 0}, {0, 0}, 0.1, 0.1},
+                   {{1, 1}, {0, 0}, 0.1, 0.1},
+                   {{1, 0}, {0, 1}, 0.2, 0.4}};
+
+        conns_1 = {{{0, 0}, {1, 0}, 0.1, 0.2},
+                   {{0, 0}, {1, 0}, 0.3, 0.1},
+                   {{0, 0}, {1, 0}, 0.1, 0.8}};
+
+        auto recipe_0 = custom_recipe({cell_0, cell_1}, {1, 2}, {2, 1}, {conns_0, conns_1}, {{}, {}});
+        auto decomp_0 = partition_load_balance(recipe_0, context);
+
+        EXPECT_NO_THROW(simulation(recipe_0, decomp_0, context));
+    }
+    {
+        conns_0 = {{{1, 0}, {0, 0}, 0.1, 0.1},
+                   {{2, 1}, {0, 0}, 0.1, 0.1},
+                   {{1, 0}, {0, 1}, 0.2, 0.4}};
+
+        conns_1 = {{{0, 0}, {1, 0}, 0.1, 0.2},
+                   {{0, 0}, {1, 0}, 0.3, 0.1},
+                   {{0, 0}, {1, 0}, 0.1, 0.8}};
+
+        auto recipe_1 = custom_recipe({cell_0, cell_1}, {1, 2}, {2, 1}, {conns_0, conns_1}, {{}, {}});
+        auto decomp_1 = partition_load_balance(recipe_1, context);
+
+        EXPECT_THROW(simulation(recipe_1, decomp_1, context), arb::bad_connection_source_gid);
+    }
+    {
+        conns_0 = {{{1, 0}, {0, 0}, 0.1, 0.1},
+                   {{1, 1}, {0, 0}, 0.1, 0.1},
+                   {{1, 3}, {0, 1}, 0.2, 0.4}};
+
+        conns_1 = {{{0, 0}, {1, 0}, 0.1, 0.2},
+                   {{0, 0}, {1, 0}, 0.3, 0.1},
+                   {{0, 0}, {1, 0}, 0.1, 0.8}};
+
+        auto recipe_2 = custom_recipe({cell_0, cell_1}, {1, 2}, {2, 1}, {conns_0, conns_1}, {{}, {}});
+        auto decomp_2 = partition_load_balance(recipe_2, context);
+
+        EXPECT_THROW(simulation(recipe_2, decomp_2, context), arb::bad_connection_source_lid);
+    }
+    {
+        conns_0 = {{{1, 0}, {0, 0}, 0.1, 0.1},
+                   {{1, 1}, {0, 0}, 0.1, 0.1},
+                   {{1, 0}, {0, 1}, 0.2, 0.4}};
+
+        conns_1 = {{{0, 0}, {1, 0}, 0.1, 0.2},
+                   {{0, 0}, {7, 0}, 0.3, 0.1},
+                   {{0, 0}, {1, 0}, 0.1, 0.8}};
+
+        auto recipe_3 = custom_recipe({cell_0, cell_1}, {1, 2}, {2, 1}, {conns_0, conns_1}, {{}, {}});
+        auto decomp_3 = partition_load_balance(recipe_3, context);
+
+        EXPECT_THROW(simulation(recipe_3, decomp_3, context), arb::bad_connection_target_gid);
+    }
+    {
+        conns_0 = {{{1, 0}, {0, 0}, 0.1, 0.1},
+                   {{1, 1}, {0, 0}, 0.1, 0.1},
+                   {{1, 0}, {0, 1}, 0.2, 0.4}};
+
+        conns_1 = {{{0, 0}, {1, 0}, 0.1, 0.2},
+                   {{0, 0}, {0, 0}, 0.3, 0.1},
+                   {{0, 0}, {1, 0}, 0.1, 0.8}};
+
+        auto recipe_5 = custom_recipe({cell_0, cell_1}, {1, 2}, {2, 1}, {conns_0, conns_1}, {{}, {}});
+        auto decomp_5 = partition_load_balance(recipe_5, context);
+
+        EXPECT_THROW(simulation(recipe_5, decomp_5, context), arb::bad_connection_target_gid);
+    }
+    {
+        conns_0 = {{{1, 0}, {0, 0}, 0.1, 0.1},
+                   {{1, 1}, {0, 0}, 0.1, 0.1},
+                   {{1, 0}, {0, 1}, 0.2, 0.4}};
+
+        conns_1 = {{{0, 0}, {1, 0}, 0.1, 0.2},
+                   {{0, 0}, {1, 9}, 0.3, 0.1},
+                   {{0, 0}, {1, 0}, 0.1, 0.8}};
+
+        auto recipe_4 = custom_recipe({cell_0, cell_1}, {1, 2}, {2, 1}, {conns_0, conns_1}, {{}, {}});
+        auto decomp_4 = partition_load_balance(recipe_4, context);
+
+        EXPECT_THROW(simulation(recipe_4, decomp_4, context), arb::bad_connection_target_lid);
+    }
+}
\ No newline at end of file