Figured it out. Took me two hours
The root cause is an ID collision.
Maraxsis has this code:
maraxsis.on_event({"build", "build-ghost", "super-forced-build"}, function(event)
maraxsis.execute_later("construct_sand_extractor", 1, event)
end)
function maraxsis.execute_later(function_key, ticks, ...)
local marked_for_death_render_object = rendering.draw_line {
color = {0, 0, 0, 0},
width = 0,
filled = false,
from = {0, 0},
to = {0, 0},
create_build_effect_smoke = false,
surface = "nauvis",
time_to_live = ticks
}
storage._delayed_functions = storage._delayed_functions or {}
storage._delayed_functions[script.register_on_object_destroyed(marked_for_death_render_object)] = {function_key, {...}}
end
This means that on every left click, even if you're holding nothing, a "render object" of a line is created that survives for 1 tick.
However, on_object_destroyed's useful_id isn't unique across different target types. It can happen that a render object's expiry happens to have a useful_id same as a tunnel's entity ID, and this code in Subsurface would trigger:
script.on_event(defines.events.on_object_destroyed, function(event)
-- entrances can't be mined, but in case they are destroyed by mods we have to handle it
if storage.pole_links[event.useful_id] and storage.pole_links[event.useful_id].valid then
local opposite_car = storage.pole_links[event.useful_id].surface.find_entities_filtered{name = {"tunnel-entrance", "tunnel-exit"}, position = storage.pole_links[event.useful_id].position, radius = 1}[1]
if opposite_car and opposite_car.valid then opposite_car.destroy() end
storage.pole_links[event.useful_id].destroy()
storage.pole_links[event.useful_id] = nil
elseif storage.car_links[event.useful_id] and storage.car_links[event.useful_id].valid then
local opposite_pole = storage.car_links[event.useful_id].surface.find_entities_filtered{name = {"tunnel-entrance-cable", "tunnel-exit-cable"}, position = storage.car_links[event.useful_id].position, radius = 1}[1]
if opposite_pole and opposite_pole.valid then opposite_pole.destroy() end
storage.car_links[event.useful_id].destroy()
storage.car_links[event.useful_id] = nil
Guarding this event handler with if event.type ~= defines.target_type.entity then return end fixes the problem for me.
If needed, I have a save where a tunnel will get deleted immediately upon the next left click, based on skunk's save.