diff --git a/arbor/backends/multicore/shared_state.cpp b/arbor/backends/multicore/shared_state.cpp
index 6936f56dcec078faa1d710da4e708de322dfa7a1..34a9853dab1c641edacea2874d6b55e89b4b7c08 100644
--- a/arbor/backends/multicore/shared_state.cpp
+++ b/arbor/backends/multicore/shared_state.cpp
@@ -430,17 +430,20 @@ std::size_t extend_width(const arb::mechanism& mech, std::size_t width) {
 void shared_state::set_parameter(mechanism& m, const std::string& key, const std::vector<arb_value_type>& values) {
     if (values.size()!=m.ppack_.width) throw arbor_internal_error("mechanism field size mismatch");
 
+    bool found = false;
     arb_value_type* data = nullptr;
     for (arb_size_type i = 0; i<m.mech_.n_parameters; ++i) {
         if (key==m.mech_.parameters[i].name) {
             data = m.ppack_.parameters[i];
+            found = true;
             break;
         }
     }
 
-    if (!data) throw arbor_internal_error(util::pprintf("no such parameter '{}'", key));
+    if (!found) throw arbor_internal_error(util::pprintf("no such parameter '{}'", key));
 
     if (!m.ppack_.width) return;
+
     auto width_padded = extend_width<arb_value_type>(m, m.ppack_.width);
     copy_extend(values, util::range_n(data, width_padded), values.back());
 }
@@ -519,9 +522,6 @@ void shared_state::instantiate(arb::mechanism& m, unsigned id, const mechanism_o
         m.ppack_.ion_states[idx] = { oion->iX_.data(), oion->eX_.data(), oion->Xi_.data(), oion->Xo_.data(), oion->charge.data() };
     }
 
-    // If there are no sites (is this ever meaningful?) there is nothing more to do.
-    if (m.ppack_.width==0) return;
-
     // Initialize state and parameter vectors with default values.
     {
         // Allocate bulk storage
@@ -567,8 +567,11 @@ void shared_state::instantiate(arb::mechanism& m, unsigned id, const mechanism_o
         store.indices_ = iarray(count*index_width_padded, 0, pad);
         chunk_writer writer(store.indices_.data(), index_width_padded);
         // Setup node indices
-        m.ppack_.node_index = writer.append(pos_data.cv, pos_data.cv.back());
-
+        //   We usually insert cv.size() == width elements into node index (length: width_padded >= width)
+        //   and pad by the last element of cv. If width == 0 we must choose a different pad, that will not
+        //   really be used, as width == width_padded == 0. Nevertheless, we need to pass it.
+        auto pad_val = pos_data.cv.empty() ? 0 : pos_data.cv.back();
+        m.ppack_.node_index = writer.append(pos_data.cv, pad_val);
         auto node_index = util::range_n(m.ppack_.node_index, index_width_padded);
         // Make SIMD index constraints and set the view
         store.constraints_ = make_constraint_partition(node_index, m.ppack_.width, m.iface_.partition_width);
diff --git a/arbor/fvm_layout.cpp b/arbor/fvm_layout.cpp
index d2ee99ce301ddf0c16f93dcba030223c3e69afc1..12ed2a343defa50f901e72fa15a0ba789d952b2e 100644
--- a/arbor/fvm_layout.cpp
+++ b/arbor/fvm_layout.cpp
@@ -943,7 +943,7 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties&
         }
 
         update_ion_support(info, config.cv);
-        M.mechanisms[name] = std::move(config);
+        if (!config.cv.empty()) M.mechanisms[name] = std::move(config);
     }
 
     // Synapses:
@@ -1085,7 +1085,7 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties&
         update_ion_support(info, config.cv);
 
         M.n_target += config.target.size();
-        M.mechanisms[name] = std::move(config);
+        if (!config.cv.empty()) M.mechanisms[name] = std::move(config);
     }
     M.post_events = post_events;
 
@@ -1136,7 +1136,7 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties&
         std::unique_copy(config.cv.begin(), config.cv.end(), std::back_inserter(config.cv_unique));
         config.cv_unique.shrink_to_fit();
 
-        M.stimuli = std::move(config);
+        if (!config.cv.empty()) M.stimuli = std::move(config);
     }
 
     // Ions:
@@ -1205,7 +1205,7 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties&
             config.init_econc[i] *= oo_cv_area;
         }
 
-        M.ions[ion] = std::move(config);
+        if (!config.cv.empty()) M.ions[ion] = std::move(config);
     }
 
     std::unordered_map<std::string, mechanism_desc> revpot_tbl;
@@ -1276,7 +1276,7 @@ fvm_mechanism_data fvm_build_mechanism_data(const cable_cell_global_properties&
                         config.param_values.emplace_back(kv.first, std::vector<value_type>(config.cv.size(), kv.second));
                     }
 
-                    M.mechanisms[revpot.name()] = std::move(config);
+                    if (!config.cv.empty()) M.mechanisms[revpot.name()] = std::move(config);
                 }
             }
         }
diff --git a/test/unit/test_abi.cpp b/test/unit/test_abi.cpp
index 5db2510b08a5d6e7658c1f819d75d8c2e1cbf15a..5cc736e179931e16fb63827bfda7210591d065cc 100644
--- a/test/unit/test_abi.cpp
+++ b/test/unit/test_abi.cpp
@@ -59,7 +59,7 @@ TEST(abi, multicore_initialisation) {
     layout.weight.assign(ncv, 1.);
     for (arb_size_type i = 0; i<ncv; ++i) layout.cv.push_back(i);
 
-    shared_state.instantiate(mech, 42, {}, layout);
+    EXPECT_NO_THROW(shared_state.instantiate(mech, 42, {}, layout));
 
     {
         ASSERT_EQ(globals.size(), mech.mech_.n_globals);
@@ -93,6 +93,53 @@ TEST(abi, multicore_initialisation) {
     }
 }
 
+TEST(abi, multicore_null) {
+    std::vector<arb_field_info> globals = {{ "G0", "kg",  123.0,     0.0, 2000.0},
+                                           { "G1", "lb",  456.0,     0.0, 2000.0},
+                                           { "G2", "gr",  789.0,     0.0, 2000.0}};
+    std::vector<arb_field_info> states  = {{ "S0", "nA",      0.123, 0.0, 2000.0},
+                                           { "S1", "mV",      0.456, 0.0, 2000.0}};
+    std::vector<arb_field_info> params  = {{ "P0", "lm", -123.0,     0.0, 2000.0}};
+
+    arb_mechanism_type type{};
+    type.abi_version = ARB_MECH_ABI_VERSION;
+    type.globals    = globals.data(); type.n_globals    = globals.size();
+    type.parameters = params.data();  type.n_parameters = params.size();
+    type.state_vars = states.data();  type.n_state_vars = states.size();
+
+    arb_mechanism_interface iface { arb_backend_kind_cpu,
+                                    1,
+                                    1,
+                                    nullptr,
+                                    nullptr,
+                                    nullptr,
+                                    nullptr,
+                                    nullptr,
+                                    nullptr };
+
+    auto mech = arb::mechanism(type, iface);
+
+    arb_size_type ncell = 1;
+    arb_size_type ncv = 0;
+    std::vector<arb_index_type> cv_to_intdom(ncv, 0);
+    std::vector<arb_value_type> temp(ncv, 23);
+    std::vector<arb_value_type> diam(ncv, 1.);
+    std::vector<arb_value_type> vinit(ncv, -65);
+    std::vector<arb::fvm_gap_junction> gj = {};
+    std::vector<arb_index_type> src_to_spike = {};
+
+    arb::multicore::shared_state shared_state(ncell, ncell, 0,
+                                              cv_to_intdom, cv_to_intdom,
+                                              gj, vinit, temp, diam, src_to_spike,
+                                              mech.data_alignment());
+
+    arb::mechanism_layout layout;
+    layout.weight.assign(ncv, 1.);
+    for (arb_size_type i = 0; i<ncv; ++i) layout.cv.push_back(i);
+
+    EXPECT_NO_THROW(shared_state.instantiate(mech, 42, {}, layout));
+}
+
 #ifdef ARB_GPU_ENABLED
 
 namespace {
@@ -155,7 +202,7 @@ TEST(abi, gpu_initialisation) {
     layout.weight.assign(ncv, 1.);
     for (arb_size_type i = 0; i<ncv; ++i) layout.cv.push_back(i);
 
-    shared_state.instantiate(mech, 42, {}, layout);
+    EXPECT_NO_THROW(shared_state.instantiate(mech, 42, {}, layout));
 
     {
         ASSERT_EQ(globals.size(), mech.mech_.n_globals);
@@ -187,4 +234,53 @@ TEST(abi, gpu_initialisation) {
         }
     }
 }
+
+TEST(abi, gpu_null) {
+    std::vector<arb_field_info> globals = {{ "G0", "kg",  123.0,     0.0, 2000.0},
+                                           { "G1", "lb",  456.0,     0.0, 2000.0},
+                                           { "G2", "gr",  789.0,     0.0, 2000.0}};
+    std::vector<arb_field_info> states  = {{ "S0", "nA",      0.123, 0.0, 2000.0},
+                                           { "S1", "mV",      0.456, 0.0, 2000.0}};
+    std::vector<arb_field_info> params  = {{ "P0", "lm", -123.0,     0.0, 2000.0}};
+
+    arb_mechanism_type type{};
+    type.abi_version = ARB_MECH_ABI_VERSION;
+    type.globals    = globals.data(); type.n_globals    = globals.size();
+    type.parameters = params.data();  type.n_parameters = params.size();
+    type.state_vars = states.data();  type.n_state_vars = states.size();
+
+    arb_mechanism_interface iface { arb_backend_kind_gpu,
+                                    1,
+                                    1,
+                                    nullptr,
+                                    nullptr,
+                                    nullptr,
+                                    nullptr,
+                                    nullptr,
+                                    nullptr };
+
+    auto mech = arb::mechanism(type, iface);
+
+    arb_size_type ncell = 1;
+    arb_size_type ncv = 0;
+    std::vector<arb_index_type> cv_to_intdom(ncv, 0);
+    std::vector<arb_value_type> temp(ncv, 23);
+    std::vector<arb_value_type> diam(ncv, 1.);
+    std::vector<arb_value_type> vinit(ncv, -65);
+    std::vector<arb::fvm_gap_junction> gj = {};
+    std::vector<arb_index_type> src_to_spike = {};
+
+    arb::gpu::shared_state shared_state(ncell, ncell, 0,
+                                        cv_to_intdom, cv_to_intdom,
+                                        gj, vinit, temp, diam, src_to_spike,
+                                        1);
+
+    arb::mechanism_layout layout;
+    layout.weight.assign(ncv, 1.);
+    for (arb_size_type i = 0; i<ncv; ++i) layout.cv.push_back(i);
+
+    EXPECT_NO_THROW(shared_state.instantiate(mech, 42, {}, layout));
+}
+
+
 #endif
diff --git a/test/unit/test_fvm_lowered.cpp b/test/unit/test_fvm_lowered.cpp
index fcaa3dd99b4214bb5ef71f325a7617235cfe634a..d82953885bec11243eec51def38a50d27bc88b0d 100644
--- a/test/unit/test_fvm_lowered.cpp
+++ b/test/unit/test_fvm_lowered.cpp
@@ -535,6 +535,33 @@ TEST(fvm_lowered, derived_mechs) {
     }
 }
 
+TEST(fvm_lowered, null_region) {
+    arb::proc_allocation resources;
+    if (auto nt = arbenv::get_env_num_threads()) {
+        resources.num_threads = nt;
+    }
+    else {
+        resources.num_threads = arbenv::thread_concurrency();
+    }
+
+    soma_cell_builder builder(6);
+    builder.add_branch(0, 100, 0.5, 0.5, 4, "dend");
+    auto cell = builder.make_cell();
+
+    cell.decorations.paint(reg::nil(), "test_kin1");
+    cell.decorations.paint(reg::nil(), "custom_kin1");
+
+    cable1d_recipe rec(cable_cell{cell});
+    rec.catalogue() = make_unit_test_catalogue();
+    rec.catalogue().derive("custom_kin1", "test_kin1", {{"tau", 20.0}});
+
+    auto ctx = make_context(resources);
+    auto decomp = partition_load_balance(rec, ctx);
+    simulation sim(rec, decomp, ctx);
+    EXPECT_NO_THROW(sim.run(30.0, 1.f/1024));
+}
+
+
 // Test that ion charge is propagated into mechanism variable.
 
 TEST(fvm_lowered, read_valence) {