Skip to main content
Use a named error for an expected failure that your application can handle. Examples include missing stock, invalid input, and a declined payment. A Result records that a function can either return a value or one of these errors. This gives each failure a stable name. Callers can respond to the error without parsing its message.

Declare an expected error

Declare each kind of failure with error. Add fields when the caller needs details about what went wrong.
These two errors remain different types even if their fields later become the same. This is called a nominal type. The generated Go types implement the standard error interface.

Start with a small error

An error can carry only the data needed to explain or handle the failure.
Here validatePositive returns NotPositive when the check fails. Generated Go returns the same error as an ordinary Go error.

Return a value or an error

A function that can fail returns Result(Success, Failure). The first type is the successful value. The second type is the error. The failure type must belong to the Error family. Forst can infer this return type when ensure introduces a failure path.
The check in okInt can fail, so Forst infers Result(Int, Error). The caller uses ensure x is Ok() before using the successful value. Ok() and Err() identify the two sides of a Result during is and ensure checks. They are currently checks. General Ok(value) and Err(error) constructors are still in development.

Return a specific error with ensure

Use ensure condition or error when a failed check should return a known error. The function stops at that point and gives the error to its caller.
The first check requires avail to contain a successful value. The second check returns InsufficientStock with details that the caller can use.

Let Forst infer the return type

Forst looks at three places when a function has no declared return type.
  • Each explicit return
  • Each ensure that can fail
  • The final expression in the function body
If the final statement is an expression, its value becomes the return value. Generated Go adds the explicit return.
Here g returns Result(Int, Error). The final call to g() means that f returns the same type. Generated Go contains return g(). Use an explicit return whenever it makes the intent easier to see.

Handle Go functions with several returns

Go functions often return a value and an error. Forst preserves all returned values when you capture the call in one variable. The resulting type is Tuple(T₁…Tₙ). Split the values and check the error when your Forst function should return a Result.
This is the same shape as ordinary Go error handling. Generated Go uses the usual value and error returns.

Result and Tuple stay separate

The two types make different promises. A Result contains one outcome. It is either a success or a failure. A Tuple preserves every returned position, and several positions may contain meaningful values at the same time.

Problems caused by mixing them

Result means that you get a value or an error. Some calls can return both. For example, a read can return its final bytes together with an end of input signal. The bytes still need to be processed. Forst cannot learn this behavior from the return types. A pair containing a value and an error looks the same in both cases. Keeping a list of special calls would miss new packages and functions. Forst therefore never turns several return values into a Result automatically. This keeps Ok() and Err() trustworthy. Ok() always means that there is no error. Err() always means that there is no success value.

How Forst keeps the meaning clear

  • A call with several returns always becomes a Tuple when captured in one value. Every returned value stays available.
  • A Result comes from a function with exclusive success and failure paths. return and ensure make those paths explicit.
  • Conversion between Tuple and Result is unavailable. Ok() and Err() only apply to Result.
  • When you know a call has exclusive outcomes, split its values and use ensure !err or err before returning the success value.
  • The rule applies to every package. It needs no catalog or project setting.
This preserves all possible states of a return with several values. It also keeps Result reliable for narrowing and for generated client types. If a call with several returns is the final expression of an inferred function, the function also returns a Tuple. Split the values as shown above when you want a Result. For calls used only for side effects, prefer Forst println or print. A Provider contract method can also declare an explicit return type.

Use errors from TypeScript

Generated TypeScript clients can expose error payloads as tagged shapes. This part of the client API is still evolving. Generate the client with forst generate and see Generate client types for the current output.

How this compares with Go

In Go, you usually declare a struct and add an Error() method. Forst’s error Name { … } declaration creates both parts from one declaration. The generated result remains an ordinary Go error.

Current limits

Named errors and Result are experimental. Check the roadmap before relying on edge cases in production.

Result failure side

The failure type must belong to the Error family. Other failure types are not supported yet.

Ok and Err role

Ok and Err support is and ensure checks. General value constructors are still in development.

Final Go calls

A final Go call with several return values produces a Tuple during return inference. Split the values and check the error when you need a Result.

Generated error details

Error creation through ensure, type merging after or, and TypeScript _tag output are still maturing. Treat generated payloads as evolving. See Generate client types. For limits around ensure x is Ok(), see Ensure and narrowing.

Examples

See the error handling examples on GitHub Learn how ensure checks a value and returns early when a condition fails.

Combining types

Define a closed set of named errors.

Ensure and narrowing

How ensure connects checks to control flow.