Skip to content

Error Handling

Chuks provides robust error handling using the familiar try-catch-finally mechanism. Errors can be raised manually using the throw statement or occur naturally during runtime.

The built-in Error class is the standard way to represent exceptions in Chuks. It is globally available — no import needed.

class Error {
public message: string // The error message (required)
public code: any // Optional error code (e.g. 404, "NOT_FOUND")
public data: any // Optional additional data
public stack: string // Stack trace (reserved for future use)
constructor(message: string, code?: any, data?: any)
function toString(): string // Returns "Error: " + message
}
// Simple error with just a message
var e1 = new Error("something went wrong")
// Error with a numeric code
var e2 = new Error("not found", 404)
// Error with code and extra data
var e3 = new Error("validation failed", "VALIDATION_ERROR", "age field")

When only the message is provided, code and data default to null.

Error instances automatically use their message when concatenated with strings:

var e = new Error("file not found")
println("Error: " + e) // Error: file not found

Use try to execute potentially failing code and catch to handle errors.

try {
const result = 10 / 0
println(result)
} catch (e) {
println("Caught an error: " + e.message)
}

Output:

Caught an error: division by zero

You can throw custom errors to indicate failure conditions.

function validateAge(age: int): void {
if (age < 0) {
throw new Error("Age cannot be negative")
}
}
try {
validateAge(-5)
} catch (e) {
println("Invalid age: " + e.message)
}

Use error codes to categorize errors programmatically:

function fetchUser(id: int): any {
if (id <= 0) {
throw new Error("Invalid user ID", 400)
}
// ... lookup user ...
throw new Error("User not found", 404)
}
try {
fetchUser(0)
} catch (e) {
println(e.message) // Invalid user ID
println(e.code) // 400
}

Attach extra context to errors for richer error handling:

function validateInput(name: string, value: int): void {
if (value < 0) {
throw new Error("Validation failed", "VALIDATION_ERROR", name)
}
}
try {
validateInput("age", -5)
} catch (e) {
println(e.message) // Validation failed
println(e.code) // VALIDATION_ERROR
println(e.data) // age
}

The finally block executes regardless of whether an error occurred or not. It is useful for cleanup operations like closing files or releasing resources.

import { fs } from "std/fs"
async function readFileSafe(path: string): void {
try {
const content = await fs.readFile(path)
println(content)
} catch (e) {
println("File read failed: " + e.message)
} finally {
println("Operation finished.")
}
}

Success output:

File content...
Operation finished.

Failure output:

File read failed: file not found
Operation finished.

To handle specific error cases, check the code or message properties inside the catch block:

try {
// ... code that may throw ...
} catch (e) {
if (e.code == 404) {
println("Resource not found")
} else if (e.code == 400) {
println("Bad request: " + e.message)
} else {
println("Unexpected error: " + e.message)
}
}

While Error objects are the recommended way to throw exceptions, Chuks also supports throwing strings and integers:

throw "something went wrong" // throws a string
throw 42 // throws an integer

However, using new Error(...) is preferred as it provides structured properties (message, code, data).

When an error is not caught by a try/catch block, the Chuks VM will halt and print a source-mapped error message. This message contains the exact file name, line number, and a descriptive error string to help you debug quickly:

test.chuks:14: property 'missing' not found on map
test.chuks:10: division by zero
test.chuks:5: unsupported operator for types