As an option, part of the deliver_to_target function could be wrapped in a try/catch block — in that case, empty planes will still arrive and get destroyed, but at least it won’t crash anymore.
local success, err = pcall(function()
local name, quality_count = next(source_scheduled)
local quality, count = next(quality_count)
if name and quality and count then
count = math.min(count, get_stack_size(name))
local target_scheduled = self.delivery_target.scheduled
local removed = self.inventory.remove({name = name, quality = quality, count = count})
if removed > 0 then
self.delivery_target.inventory.insert({name = name, quality = quality, count = removed})
end
source_scheduled[name][quality] = source_scheduled[name][quality] - count
if source_scheduled[name][quality] <= 0 then
source_scheduled[name][quality] = nil
end
if not next(source_scheduled[name]) then
source_scheduled[name] = nil
end
if target_scheduled[name] and target_scheduled[name][quality] then
target_scheduled[name][quality] = target_scheduled[name][quality] - count
if target_scheduled[name][quality] <= 0 then
target_scheduled[name][quality] = nil
end
if not next(target_scheduled[name]) then
target_scheduled[name] = nil
end
end
delivery_time = self:make_delivery_particle(name)
end
end)