Error Handling
Vibe uses a unified value system where every value can carry an error. This allows errors to propagate through expressions without crashing your program.
Error Properties
Section titled “Error Properties”Every value in Vibe can carry error information. You can check for errors using these properties:
.err- Boolean flag (trueif error,falseif successful).errDetails- Error information (when.erristrue).toolCalls- Record of AI tool calls (for AI responses)
Checking for Errors
Section titled “Checking for Errors”Access the .err property to check if a value has an error. Since .err is a boolean, you can use it directly in conditions:
let result = vibe "Do something that might fail"
if result.err { let message = result.errDetails.message // Handle the error}Error Properties
Section titled “Error Properties”When .err is true, use .errDetails to access error information:
message- Human-readable error descriptiontype- Error class name (e.g., “TypeError”, “ReferenceError”)location- Source location where the error occurred (if available)
if result.err { let errorType = result.errDetails.type // "TypeError" let errorMsg = result.errDetails.message // "Expected number, got text"}Error Propagation
Section titled “Error Propagation”Errors automatically propagate through expressions. If any operand has an error, the result will also have that error:
let a: number = vibe "Return a number" // Might faillet b = 10
// If 'a' has an error, 'sum' will have the same errorlet sum = a + b
// Check at the endif sum.err { // Handle error}This means you don’t need to check every intermediate value—errors flow through naturally.
Throwing Errors
Section titled “Throwing Errors”Use throw to create an error and return immediately from a function:
function divide(a: number, b: number): number { if b == 0 { throw "Division by zero" } return a / b}
let result = divide(10, 0)if result.err { print(result.errDetails.message) // "Division by zero"}When throw executes:
- The function returns immediately (code after
throwis not executed) - The return value has
.err = trueand.errDetails.messageset to your message - The caller can check for errors like any other operation
Throw with Expressions
Section titled “Throw with Expressions”The throw message can be any expression:
function validateAge(age: number): number { if age < 0 { throw "Age cannot be negative: " + age } if age > 150 { throw "Age seems unrealistic: " + age } return age}Errors Propagate from Throw
Section titled “Errors Propagate from Throw”Thrown errors propagate through expressions just like any other error:
function inner(): number { throw "Something went wrong"}
function outer(): number { let x = inner() // x has the error return x + 1 // Error propagates through addition}
let result = outer()print(result.err) // trueprint(result.errDetails.message) // "Something went wrong"Top-Level Throw
Section titled “Top-Level Throw”You can also throw at the top level to stop execution:
let config = loadConfig()if config.err { throw "Failed to load config: " + config.errDetails.message}
// Rest of program only runs if config loaded successfullyAI Response Errors
Section titled “AI Response Errors”AI calls can fail for various reasons:
- API errors (rate limits, authentication, network issues)
- Type validation failures (AI returned wrong type)
let count: number = vibe "How many items?"
if count.err { // AI might have returned text instead of a number // Or the API call might have failed let fallback = 0}Tool Call Records
Section titled “Tool Call Records”When AI uses tools, the tool calls are recorded in the .toolCalls property for inspection. Tool execution errors are sent back to the AI model, which can retry or try a different approach—they don’t automatically make the value have an error.
let result = vibe "Search for information and summarize"
// Inspect tool calls that occurredfor call in result.toolCalls { if call.err { // This tool call failed, but AI received the error and continued let message = call.errDetails.message }}Each tool call record contains:
toolName- Name of the toolargs- Arguments passed to the toolresult- Return value (if successful)err- Boolean indicating if this call failederrDetails- Error details whenerris true (hasmessageproperty)duration- Execution time in milliseconds
Patterns
Section titled “Patterns”Default Values
Section titled “Default Values”Provide a fallback when errors occur:
let count: number = vibe "How many items?"
// Use default if errorlet safeCount = countif count.err { safeCount = 0}Guard Clauses
Section titled “Guard Clauses”Check for errors early and handle them:
let data: json = vibe "Fetch the user data"
if data.err { // Log and exit early return null}
// Continue with valid datalet name = data.nameTypeScript Error Handling
Section titled “TypeScript Error Handling”Check errors in Vibe, then use TypeScript blocks for complex logic:
let result = vibe "Risky operation"
if result.err { let msg = result.errDetails.message ts(msg) { console.error(`Operation failed: ${msg}`); }} else { let data = result ts(data) { // Process the successful result console.log(`Got data: ${JSON.stringify(data)}`); }}Note: TypeScript blocks receive resolved values. Check .err in Vibe code before passing values to ts blocks.
Parallel Operation Errors
Section titled “Parallel Operation Errors”With async let, each parallel operation captures its own errors independently:
async let user: json = fetchUser(userId)async let posts: json[] = fetchPosts(userId)async let notifications: json[] = getNotifications(userId)
// Each can succeed or fail independentlyif user.err { print("Failed to load user")}if posts.err { print("Failed to load posts")}if notifications.err { print("Notifications unavailable")}Best Practices
Section titled “Best Practices”Check Critical Operations
Section titled “Check Critical Operations”Always check errors for operations that must succeed:
let config: json = vibe "Load configuration"
if config.err { // Can't continue without config return}Let Non-Critical Errors Flow
Section titled “Let Non-Critical Errors Flow”For non-critical operations, let errors propagate and check at the end:
let step1 = vibe "Step 1"let step2 = vibe "Step 2 using {step1}"let step3 = vibe "Step 3 using {step2}"
// Check only at the endif step3.err { // Something failed along the way}Provide Context in Error Messages
Section titled “Provide Context in Error Messages”When logging errors, add context:
let userData: json = vibe "Fetch user {userId}"
if userData.err { let msg = userData.errDetails.message print('Failed to fetch user {userId}: {msg}')}