> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lionlang.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Handling Lion evaluation errors in TypeScript

> Learn how Lion surfaces typed evaluation errors through Effect so you can handle parse failures, argument mismatches, and invalid calls safely.

Lion uses [Effect](https://effect.website) to model computation, which means errors are values tracked in the type system rather than exceptions thrown at runtime. When something goes wrong during evaluation — a bad argument, an unresolvable function call, or a malformed expression — Lion fails the Effect with a typed error that you can match, recover from, or propagate without a try/catch anywhere in sight.

## The EvaluateResult type

The return type of `run` and the internal `evaluate` function is `EvaluateResult`:

```typescript theme={null}
import type { Effect } from "effect";
import type { ParseError } from "effect/ParseResult";
import type { ArgumentMismatchError, InvalidFunctionCallError } from "@lionlang/core/errors/evaluation";
import type { LionEnvironmentService } from "@lionlang/core/services/evaluation";

type EvaluateResult = Effect.Effect<
  unknown,
  ParseError | ArgumentMismatchError | InvalidFunctionCallError,
  LionEnvironmentService
>;
```

<Note>
  `EvaluateResult` is `Effect<unknown, ParseError | ArgumentMismatchError | InvalidFunctionCallError, LionEnvironmentService>`. The three type parameters are the success value, the union of possible errors, and the required service. `run` provides `LionEnvironmentService` automatically from the environment you pass, so by the time you reach `Effect.runPromise` the requirement is fully satisfied.
</Note>

## Error types

### `ParseError`

Produced by Effect Schema when the input to `run` does not match `LionExpressionSchema`. This happens before any evaluation occurs — the input is not a valid Lion expression at all. A `ParseError` carries a structured description of what was expected versus what was received.

### `ArgumentMismatchError`

Raised when a stdlib function receives arguments of the wrong type. For example, passing a string where a number is required in `number/add`. The `_tag` field is `"ArgumentMismatchError"`.

### `InvalidFunctionCallError`

Raised when an array expression's first element does not resolve to a callable value. If Lion evaluates `["notAFunction", 1, 2]` and `notAFunction` is not a function in the environment, evaluation fails with this error. The `expression` field contains the original Lion expression for debugging. The `_tag` field is `"InvalidFunctionCallError"`.

## Handling all errors with Effect.catchAll

`Effect.catchAll` intercepts any failure in the Effect and lets you return a fallback value or a new Effect:

```typescript theme={null}
import { Effect } from "effect";
import { run } from "@lionlang/core/evaluation/evaluate";
import { stdlib } from "@lionlang/core/modules";

const program = run(["number/add", "not-a-number", 2], stdlib);

const result = await Effect.runPromise(
  Effect.catchAll(program, (error) =>
    Effect.succeed(`Error: ${error._tag}`)
  )
);

// => "Error: ArgumentMismatchError"
```

The `error` parameter is typed as `ParseError | ArgumentMismatchError | InvalidFunctionCallError`, so your editor will offer completions for all available fields.

## Handling specific error types with Effect.catchTag

When you only want to recover from one kind of error, use `Effect.catchTag`. Tags correspond to the class names: `"ArgumentMismatchError"`, `"InvalidFunctionCallError"`, and `"ParseError"`.

```typescript theme={null}
import { Effect } from "effect";
import { run } from "@lionlang/core/evaluation/evaluate";
import { stdlib } from "@lionlang/core/modules";

const program = run(["object/call-method", "notAnObject", "toString"], stdlib);

const result = await Effect.runPromise(
  program.pipe(
    Effect.catchTag("InvalidFunctionCallError", (error) => {
      console.error("Head of expression did not resolve to a function:", error.expression);
      return Effect.succeed(null);
    }),
    Effect.catchTag("ArgumentMismatchError", (error) => {
      console.error("Wrong argument types:", error._tag);
      return Effect.succeed(null);
    })
  )
);
```

Errors not matched by a `catchTag` remain in the failure channel and propagate to the next handler or to `runPromise`, which will reject the returned Promise.

## Surfacing parse errors separately

`ParseError` arrives from Effect Schema's `Schema.decodeUnknown` call inside `run`. You can intercept it before the other errors to give users a distinct message when the input is structurally invalid:

```typescript theme={null}
import { Effect } from "effect";
import { run } from "@lionlang/core/evaluation/evaluate";
import { stdlib } from "@lionlang/core/modules";

const invalidExpression = { notALionExpression: true };

const result = await Effect.runPromise(
  run(invalidExpression, stdlib).pipe(
    Effect.catchTag("ParseError", (_error) =>
      Effect.succeed("Input is not a valid Lion expression")
    ),
    Effect.catchAll((_error) =>
      Effect.succeed("Evaluation failed")
    )
  )
);

// => "Input is not a valid Lion expression"
```

<Warning>
  `ParseError` from Effect Schema does not have a `_tag` property matching `"ParseError"` in the same way as the Lion-specific errors. Catch it with `Effect.catchTag("ParseError", ...)` or use `Effect.catchAll` to handle all errors uniformly.
</Warning>

## Letting errors propagate as rejected Promises

If you prefer to handle errors in a traditional try/catch, do not add any `catchAll` or `catchTag` — `Effect.runPromise` will reject the Promise when the Effect fails:

```typescript theme={null}
import { Effect } from "effect";
import { run } from "@lionlang/core/evaluation/evaluate";
import { stdlib } from "@lionlang/core/modules";

try {
  const result = await Effect.runPromise(
    run(["number/add", "not-a-number", 2], stdlib)
  );
} catch (error) {
  // error is the ArgumentMismatchError instance
  console.error(error);
}
```

This works but loses the compile-time exhaustiveness guarantee that Effect provides. Prefer the typed Effect combinators when possible.
