I would suggest that you reconsider whether a channel architecture is really cleaner after all.
It is usually much easier to reason about functions that accept and return values than about long-running goroutines.
• an iterator function (instead of a channel) that returns the next value when called
• a synchronous “process” function that returns a result and error, and
• a synchronous “add” method that accumulates the result.
That results in a somewhat larger “func main”, but makes the error behavior much clearer and substantially reduces the risk of things like deadlocks and goroutine leaks.