Skip to main content
Ladybird avoids C-style error codes and exceptions in favour of a consistent, value-based error-handling model built around the ErrorOr<T> return type. Two macros — TRY and MUST — dramatically reduce the boilerplate required to propagate errors through a call stack. Alongside these, a set of custom smart pointer types makes ownership explicit in every function signature and eliminates entire classes of memory bugs.

Error Handling with TRY and MUST

TRY(…)

The TRY(...) macro is the primary mechanism for propagating errors in Ladybird. Any expression wrapped in TRY is evaluated; if it returns an error, that error is immediately returned from the enclosing function. If it succeeds, the unwrapped value becomes the result of the macro expression. This is conceptually similar to the ? operator in Rust. Example from LibGfx:
Both Core::AnonymousBuffer::create_with_size and Bitmap::create_with_anonymous_buffer return ErrorOr<...>. If either fails, the error is returned immediately without any explicit if-checking.

MUST(…)

MUST(...) is similar to TRY, except that instead of propagating an error it asserts (crashes the program) if the wrapped expression fails. Use it only when you have determined through other logic that the operation genuinely cannot fail, or when a failure would be severe enough to warrant an immediate abort.
MUST(...) is not a substitute for TRY(...) in places where error propagation is merely inconvenient. When you cannot propagate errors right now but the call site should eventually do so, use release_value_but_fixme_should_propagate_errors() on the ErrorOr<> instead. This marks the location for future improvement without silently swallowing errors.

Fallible Constructors

Standard C++ constructors cannot return an ErrorOr<T>, which means they cannot participate in the TRY-based error propagation model. Classes that need to perform fallible operations during construction use a static factory function (conventionally named create) instead. The create function handles all fallible pre-construction work, constructs the object via a private constructor, runs any fallible post-construction steps, and returns the completed object as ErrorOr<T> or ErrorOr<NonnullOwnPtr<T>>.

Smart Pointers

Ladybird’s AK library provides three smart pointer types. Each one makes ownership semantics explicit in the type itself, guards against memory leaks, and prevents use-after-free bugs.
OwnPtr<T> represents single ownership: exactly one OwnPtr owns the pointee, and the pointee is deleted when that OwnPtr goes out of scope. These pointers are move-only — they cannot be copied.NonnullOwnPtr<T> is a variant of OwnPtr that cannot hold null. It is appropriate as a return type for functions that are guaranteed to return a valid object, and as a parameter type when ownership is transferred and null is not permitted.A NonnullOwnPtr can be assigned to an OwnPtr, but not vice versa. To promote a known-non-null OwnPtr to NonnullOwnPtr, call OwnPtr::release_nonnull().Preferred construction — helper functions:
Manual construction (when helper functions cannot access a private constructor):
Always prefer helper functions over manual construction.
RefPtr<T> implements shared ownership via reference counting. All RefPtr instances pointing to the same object share ownership; the object is deleted when the last RefPtr to it is destroyed.NonnullRefPtr<T> is the non-null variant, suitable as a return type or parameter type where null is not valid.To be held by RefPtr, a class must implement ref() and unref(). The easiest way to satisfy this requirement is to inherit from RefCounted<T>:
Preferred construction — helper functions:
Manual construction:
A NonnullRefPtr can be assigned to a RefPtr. To promote a known-non-null RefPtr, call RefPtr::release_nonnull() or dereference it with operator*.
WeakPtr<T> holds a non-owning reference to an object. When the pointee is destroyed, the WeakPtr automatically becomes null — making it safe to hold a reference to an object without extending its lifetime.To allow a class to be weakly pointed to, inherit from Weakable<T> and call make_weak_ptr() to create a WeakPtr:

Choosing the Right Pointer Type


Other Notable Patterns

ladybird_main Entry Point

Ladybird executables expose ladybird_main(Main::Arguments) instead of a standard int main(int, char**). The Main::Arguments struct provides arguments idiomatically, and the ErrorOr<int> return type allows seamless error propagation via TRY from the very top of the program. The LibMain library provides the standard main entry point that calls into ladybird_main.

String View Literals

AK::StringView supports the ""sv literal operator, allowing string views to be constructed from literals at zero runtime cost (the length is computed at compile time and the data lives in the binary’s data section):

AK::SourceLocation

AK::SourceLocation (Ladybird’s equivalent of C++20 std::source_location) can be added as a default argument to capture the caller’s file, line, and function name without preprocessor macros: