Robust Error Handling in Mojo

Mojo

Mojo uses explicit error returns rather than stack unwinding. A function that may fail declares raises, callers either handle the error with try/except or propagate it, and a raised value can be a general Error or a typed error.

The core error model

A failable function marks its signature with raises:

fn parse_positive(value: Int) raises -> Int:
    if value <= 0:
        raise Error("value must be positive")
    return value

The caller can catch the error:

def main():
    try:
        var value = parse_positive(-1)
        print(value)
    except e:
        print("Validation failed:", e)

Unlike Python, current Mojo does not use multiple typed except SomeError as e clauses. A try statement has one except clause, and the caught value’s type is inferred.

Use else and finally deliberately

fn load_value() raises -> Int:
    return 42


def main():
    try:
        var value = load_value()
    except e:
        print("Load failed:", e)
    else:
        print("Loaded:", value)
    finally:
        print("Finished")

else runs only when the protected code succeeds. finally runs after either outcome, making it useful for cleanup that must always occur. Keep the try body narrow so it is obvious which operation failed.

Propagate or rethrow errors

A raises function can let another error propagate. If you catch an error to add logging and then need to rethrow the same value, use the transfer operator required by Mojo’s ownership model:

fn load_configuration() raises -> String:
    try:
        return read_configuration_file()
    except e:
        print("Could not load configuration")
        raise e^

Do not catch an error merely to hide it or return a plausible default. Propagate failures that the current layer cannot meaningfully handle.

Define a typed error

Typed errors are structs that satisfy the error requirements. They are useful when callers need structured context rather than a message alone:

from std.io import Writer


@fieldwise_init
struct ValidationError(Copyable, Writable):
    var field: String
    var reason: String

    def write_to(self, mut writer: Some[Writer]):
        writer.write(
            "ValidationError(", self.field, "): ", self.reason
        )


fn validate_username(
    username: String,
) raises ValidationError -> String:
    if username.count_codepoints() < 3:
        raise ValidationError("username", "must have 3 characters")
    return username

Use a general Error for simple failures and a typed error when structured fields improve diagnostics or handling. Avoid recreating Python exception class hierarchies in Mojo.

Practical guidelines

  • Mark failure explicitly with raises.
  • Validate input at boundaries and include actionable context.
  • Keep try blocks small.
  • Use finally for mandatory cleanup.
  • Propagate errors when the current function cannot recover safely.

Official References

Comment

Copied title and URL