There's many options, just look at other modern languages. The simplest are exceptions/errors that can be thrown - like you get in Java, python, typescript, etc. For something more like a traditional return value, you have std::result in rust, and error union types in zig, with language support to guarantee you check and handle the result. In C, you have macros and goto that can be used to reduce boilerplate and implement whatever pattern you want.