Documentation

NXD Error Handling Specification

Back to documentation index
ERROR_HANDLING.md

# NXD Error Handling Specification

NXD defines a unified, semantic error model that works consistently across Nim, Elixir, and D.
The user sees one system, while backends map it to their native mechanisms.

## NXD error handling is built on three pillars:

• Result types — for recoverable errors
• Option types — for nullable/optional values
• Exceptions — for exceptional or unrecoverable conditions
• Try/Catch/Finally — structured error control flow


1. Error categories

NXD defines three categories of errors:

### Recoverable errors

Represented using the RESULT type:

TYPE RESULT UNION { OK(any), ERR(string) }


Used for:

• validation failures
• IO errors
• user‑level recoverable conditions
• domain errors


2. Optional absence

Represented using the OPTION type:

TYPE OPTION UNION { SOME(any), NONE }


Used for:

• missing values
• optional fields
• nullable semantics


3. Exceptional errors

Represented using THROW and TRY/CATCH.

Used for:

• unexpected conditions
• invariants
• runtime failures
• system errors


2. Result type semantics

### Construction

```nxd
OK(V)
ERR(MSG)
```

### Consumption

### Pattern matching:

```nxd
MATCH R:
    CASE OK(V):
        ...
    CASE ERR(E):
        ...
```

### Helpers

```nxd
• IS_OK(R)
• IS_ERR(R)
• UNWRAP_OR(R, DEFAULT)
• MAP_OK(R, FN)
```

### Philosophy

Recoverable errors should use RESULT.
Exceptions should be reserved for truly exceptional conditions.


3. Option type semantics

### Construction

```nxd
SOME(V)
NONE
```

### Consumption

```nxd
MATCH O:
    CASE SOME(V):
        ...
    CASE NONE:
        ...
```

### Helpers

```nxd
• IS_SOME(O)
• IS_NONE(O)
• UNWRAP_OR(O, DEFAULT)
```

### Philosophy

Use OPTION when absence is expected and not an error.


4. Exceptions

NXD supports exceptions for exceptional conditions.

### Throwing

THROW "Something went wrong"


### Catching

```nxd
TRY:
    ...
CATCH E:
    ...
FINALLY:
    ...
```

### Semantics

• THROW immediately unwinds the current call stack.
• CATCH binds the error message or error object.
• FINALLY always executes.


### Philosophy

Exceptions are for unexpected or unrecoverable conditions.
They should not be used for normal control flow.


5. Backend mapping

### Nim

```nim
• RESULT → Result[T]
• OPTION → Option[T] or ref T
• THROW → raise newException
• TRY/CATCH → try/except/finally
```

### Elixir

```elixir
• RESULT → {:ok, v} / {:error, msg}
• OPTION → {:some, v} / :none or nil
• THROW → raise
• TRY/CATCH → try/rescue/after
```

### D

```d
• RESULT → Result!T
• OPTION → Nullable!T
• THROW → throw new Exception
• TRY/CATCH → try/catch/finally
```

Backends preserve semantics even if their native mechanisms differ.


6. Error propagation rules

### Result propagation

Functions returning RESULT must propagate errors explicitly:

```nxd
FUNC LOAD_FILE(PATH):
    LET R SET READ(PATH)
    MATCH R:
        CASE OK(DATA):
            RETURN OK(DATA)
        CASE ERR(E):
            RETURN ERR(E)
```

### Option propagation

```nim
FUNC FIRST(LIST):
    IF LIST IS EMPTY:
        RETURN NONE
    OTHERWISE:
        RETURN SOME(LIST[0])
```

### Exception propagation

Exceptions propagate automatically unless caught.


7. Concurrency error rules

### Process errors

If a process throws an exception:

• It terminates.
• Its parent may receive a PROCESS_ERR message (future spec).
• Tasks wrapping processes convert exceptions into ERR.


### Task errors

If a task fails:

LET T SET TASK(FN)
LET R SET AWAIT T


R becomes:

ERR("task failed: ...")


### Channel errors

Sending to a closed channel:

• Raises an exception.


Receiving from a closed channel:

• Returns ERR("channel closed").


8. Error safety guarantees

### NXD guarantees:

• No silent failures.
• No implicit null dereferencing.
• No implicit exception swallowing.
• All error paths are visible in IR for audit agents.
• Unsafe blocks must declare error behavior explicitly.


9. Example: unified error handling

### NXD

```nxd
FUNC DIVIDE(X, Y):
    IF Y EQ 0:
        RETURN ERR("division by zero")
    OTHERWISE:
        RETURN OK(X DIV Y)

FUNC SAFE_DIVIDE(X, Y):
    LET R SET DIVIDE(X, Y)
    MATCH R:
        CASE OK(V):
            RETURN V
        CASE ERR(E):
            THROW E
```

### Nim

```nim
proc divide(x, y: int): Result[int] =
  if y == 0:
    err("division by zero")
  else:
    ok(x div y)

proc safeDivide(x, y: int): int =
  let r = divide(x, y)
  if r.isOk:
    r.ok
  else:
    raise newException(ValueError, r.err)
```

### Elixir

```elixir
def divide(x, 0), do: {:error, "division by zero"}
def divide(x, y), do: {:ok, div(x, y)}

def safe_divide(x, y) do
  case divide(x, y) do
    {:ok, v} -> v
    {:error, e} -> raise e
  end
end
```

### D

```d
Result!int divide(int x, int y) {
    if (y == 0) return err!int("division by zero");
    return ok!int(x / y);
}

int safeDivide(int x, int y) {
    auto r = divide(x, y);
    if (r.isOk) return r.ok;
    throw new Exception(r.err);
}
```

## Summary

NXD error handling is:

• Unified across all backends
• Semantic, not backend‑specific
• Explicit, with clear control flow
• Typed, via RESULT and OPTION
• Structured, via TRY/CATCH/FINALLY
• Safe, with no silent failures
• Auditable, visible in IR for agents


This system is the backbone of NXD’s reliability and security guarantees.