Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Exception Handling

JavaScript reports errors through exceptions. Lykn inherits the mechanism directly — throw, try, catch, finally are kernel forms with no surface transformation.

throw

(throw (new Error "something went wrong"))
(throw (new TypeError "expected a number"))
throw new Error("something went wrong");
throw new TypeError("expected a number");

Always throw Error instances — not strings, not numbers, not objects. Error provides a stack trace; a thrown string does not.

try / catch / finally

(try
  (bind data (parse-json input))
  (process data)
  (catch e
    (console:error "Parse failed:" e:message))
  (finally
    (cleanup)))
try {
  const data = parseJson(input);
  process(data);
} catch (e) {
  console.error("Parse failed:", e.message);
} finally {
  cleanup();
}

catch binds the error to a name (e). finally runs regardless of whether an error occurred. Both are optional, but you need at least one.

Error Chaining

ES2022’s cause property lets you wrap errors with context:

(try
  (bind config (load-config path))
  (catch e
    (throw (new Error
      (template "Failed to load config from " path)
      (obj :cause e)))))

The inner error is preserved as e.cause. This produces error chains that show both what failed and why — invaluable in production debugging.

Error Types

JavaScript provides a hierarchy of error classes:

TypeWhen to use
ErrorGeneral-purpose errors
TypeErrorWrong type (also used by Lykn’s type checks)
RangeErrorValue out of range
SyntaxErrorMalformed input
ReferenceErrorUndefined variable (rarely thrown manually)

Lykn doesn’t add custom error classes — zero runtime dependencies. Contract violations (Ch 8) use Error with structured messages. Type violations use TypeError.

Never Empty Catches

;; BAD — swallows the error silently
(try
  (risky-operation)
  (catch e))

;; GOOD — handle, rethrow, or both
(try
  (risky-operation)
  (catch e
    (console:error "Operation failed:" e:message)
    (throw e)))

An empty catch is a bug waiting to happen. If you catch an error, do something with it.

The Functional Alternative

Exceptions are for unexpected failures — bugs, network errors, resource exhaustion. For expected failures — “user not found,” “invalid input,” “file doesn’t exist” — Lykn offers a different pattern: Result types.

;; Preview — covered fully in Ch 10
(type Result
  (Ok :any value)
  (Err :string message))

(func find-user
  :args (:string id)
  :returns :any
  :body (if (users:has id)
          (Ok (users:get id))
          (Err "user not found")))

A Result is a value, not a control flow disruption. You handle it with match, and the compiler ensures you handle both cases. Exceptions unwind the stack; Result values flow through normal returns.

The guidance: exceptions for bugs (things that shouldn’t happen), Result for expected failures (things that might happen). This is a Rust pattern that Lykn adopts — not the only way, but the Lykn way.