Instead of returning awkward untyped error strings, return real error structs. This will allow users of the library to extract interesting semantic meaning from our errors, and is just all around less awful than what we had before.