An error to me can be as simple as any time I need to write a range check so I don't index out of bounds, that very act of range checking is an error check. In other words, any time you require a runtime check to prevent an operation that would otherwise be erroneous is an error

Philosophically, I don't differentiate how you represent errors, they are all errors in the end. You can use unchecked exceptions, results, checked exceptions, error codes, whatever. I will abide by rules set in the code base's guidelines, and I do have my preferences (unchecked exceptions), and I'll also accept using one or the other for practical reasons (performance, or binary overhead), but I will never argue that it is absolutely best that one error handling scheme is reserved for uses ABC, and the other best for EFG.

A bug is one of two things, writing code that is erroneous despite having knowledge at compile time that it can't happen such as, indexing into an array of known bounds with inputs that are known to never be out of bounds. Typically where asserts are used. You can promote these types of bugs to errors if it doesn't harm your system stability, and they often are in order to avoid crashing. The other definition of a bug is just your program entering in an unforseen state.

I dislike using the words expected, unexpected, and such for error handling unless you're talking about c++'s std::expected, because no one can agree what they mean, hence to me are useless definitions when talking about error handling. While the same can be said about definitions of errors and bugs, the practical uses tend to converge to an agreement on whether something is an error, or a bug.

From years of observing error handling:

- Most times I see people talk about it, from what I interpret from what people say, seems to have an emphasis on what operations can error out, rather than emphasizing what the state of your program should be when a unit of code errors out, which I believe should be more emphasized.

I typically define a unit of code as a function because that's how most things already work and is easier to reason about. In other words, stop focusing on individual failures of the callers, rather treat the entirety of the callee as a failure. This reduces your function to usually only have 2 states, what should your program state be when everything succeeds, and what should it be when it fails.

That's how you can interpret a lot of value based error handling functions already no matter how many operations can fail. With exceptions it means that, either your whole function must be in a try/catch block, or it doesn't have one at all.

- Value based error handling seems to re-invent some forms of unchecked exception features, with very good reasons, leading me to believe that they are on the same sides of the coin where given enough language and tooling support, they converge to the exact same thing.

- I do think it's better to encode preconditions in some way visible to callers that's not just through documentation. If you're using types, which is my preferred way, known as dependent types. I'm not too familiar with them, but apparently requires a proof solver for it to work entirely at compile time. I wouldn't mind them turning into runtime checks, and if you have overloading enabled, allows runtime checks to mostly occur once, at construction time.