Runtime Diagnostics & Error Reference
BramandScript features a compiler and virtual machine diagnostic engine inspired by the Rust compiler. When an issue occurs at tokenization, compilation, or bytecode execution, the system emits an unambiguous, contextual diagnostic featuring line numbering, 5-line context windows, column-accurate caret spans, zero-noise borderless formatting, and actionable suggestions.
5 lines of source context above and below the error site allow instant orientation without opening external editors.
Multi-character token spans are accurately underlined with ^~~~~ indicators pointing to the offending expression.
Intelligent heuristics analyze symbol scopes, mistyped keywords, and missing permissions to suggest instant fixes.
Visual Anatomy of a BramandScript Diagnostic
Every diagnostic rendered by diagnostics/diagnostics.go adheres to an international terminal standard designed for readability and terminal piping:
✦ error[CompileError]: undefined variable 'totalScore'
--> src/analytics/engine.bmd:14:13
|
9 | let multiplier = 1.25;
10 | let baseWeight = 40;
11 | let rawScore = calculate_raw(records);
12 |
13 | // Calculate adjusted score with dynamic penalty
14 | let res = totalScore * multiplier;
| ^~~~~~~~~~ [Hint: Declare variable using 'let totalScore = ...' or check spelling]
15 | return res;
16 | };
|
✦ error[Kind] followed by the specific failure message.
--> filepath:line:column which modern IDE terminals (VS Code, Alacritty, iTerm, Termux) automatically hyperlink for 1-click jumping.
| with dimmed line numbers for context lines and highlighted bold numbers for the active line.
^ points to the exact column, while tildes ~~~~ underline multi-character identifiers or operators.
[Hint: ...] with the actionable code modification needed to fix the bug immediately.
1. Syntax Errors (SyntaxError)
Syntax errors are detected during lexical analysis (lexer) and top-down operator precedence parsing (parser). They indicate malformed tokens, unclosed string literals, missing delimiters, or invalid grammar constructs.
Case 1.1: Missing Semicolon or Delimiter
BramandScript requires statement-terminating semicolons ; for all variable declarations, reassignments, import statements, and expression statements.
let count = 10
let maxLimit = 100;
let count = 10;
let maxLimit = 100;
Case 1.2: Unmatched Parentheses, Brackets, or Braces
Unbalanced delimiters prevent the Pratt parser from closing compound expressions or block scopes.
let items = [1, 2, 3;
let sum = (a + b;
let items = [1, 2, 3];
let sum = (a + b);
Case 1.3: Unclosed String Literal or Invalid Escape Sequence
String literals enclosed in double quotes must be closed on the same line or use escaped newline \n sequences.
let msg = "Hello, world;
let badPath = "C:\new_folder\test";
let msg = "Hello, world";
let badPath = "C:\\new_folder\\test";
2. Compile Errors (CompileError)
Compile errors are caught during the AST-to-bytecode compilation phase. The compiler validates symbol resolution, variable mutability, scope binding, module dependencies, and constant tables before emitting VM opcodes.
Case 2.1: Undefined Variable or Typo
Referencing a variable that has not been defined in the current block, function, or global scope triggers an immediate compilation stop.
let radius = 5;
let area = pi * radius * radius;
import "std/math" as math;
let radius = 5;
let area = math.PI * radius * radius;
--> main.bmd:2:12
| let area = pi * radius * radius;
| ^^ [Hint: Declare variable using 'let pi = ...' or check spelling]
Case 2.2: Reassignment to Constant (const)
Identifiers declared with the const keyword are strictly immutable. Attempting reassignment or compound mutation is caught at compile time.
const MAX_CONNS = 100;
MAX_CONNS = 200; // error!
let maxConns = 100;
maxConns = 200; // valid with 'let'
--> server.bmd:2:1
| MAX_CONNS = 200;
| ^^^^^^^^^ [Hint: Variables declared with 'const' cannot be modified. Use 'let' instead]
Case 2.3: Circular Module Imports & Unresolved Modules
The BramandScript module loader implements strict circular dependency detection to prevent infinite recursion during compilation.
// In module_a.bmd:
import "./module_b" as b;
// In module_b.bmd:
import "./module_a" as a; // Circular Import Error!
--> module_b.bmd:1:1
Solution: Extract shared types and utilities into an independent module (e.g. types.bmd or common.bmd) and import that from both modules.
3. Runtime Errors (RuntimeError)
Runtime errors occur during virtual machine opcode execution when an operation encounters an illegal state (e.g., division by zero, index out of range, invocation of a null reference).
Case 3.1: Division by Zero (OpDiv)
Integer or floating-point division by 0 halts execution with an explicit error.
let count = 0;
let average = total / count;
let count = 0;
let average = count == 0 ? 0 : total / count;
--> math_util.bmd:2:23
| let average = total / count;
| ^ [Hint: Denominator cannot be 0]
Case 3.2: Array Index Out of Bounds
Attempting to read or assign to an array index outside the valid range 0 <= idx < len(arr) (or negative index -len(arr) <= idx < 0).
let list = [10, 20, 30];
let val = list[5]; // out of bounds!
let list = [10, 20, 30];
let idx = 5;
let val = idx < len(list) ? list[idx] : null;
--> array_demo.bmd:2:11
| let val = list[5];
| ^ [Hint: Array index is outside the valid range (0 to len - 1)]
Case 3.3: Invoking Null or Non-Callable Reference
Attempting to execute foo() when foo is null or a non-function value.
let callback = null;
callback("data"); // crashes!
let callback = null;
if callback != null {
callback("data");
}
--> handler.bmd:2:1
| callback("data");
| ^^^^^^^^ [Hint: Verify target function is not null, or use optional chaining (?.)]
4. Sandbox Security & Permissions (PermissionDenied)
BramandScript enforces an enterprise zero-trust security sandbox by default. File system writes, socket networking, subprocess execution, and elevated file reads require explicit command-line permission flags or package contract declarations.
| Permission Flag | Protected Operations | Default Policy |
|---|---|---|
| --allow-read | file_read, file_exists, dir_list, fs_walk | Allowed in local working directory |
| --allow-write | file_write, file_append, file_delete, dir_create | Denied by default |
| --allow-net | tcp_dial, tcp_listen, http_server, ws_dial, http_fetch | Denied by default |
| --allow-run | exec, process_spawn, shell command invocation | Denied by default |
| --allow-all | Grants complete system access across all sub-systems | Explicit opt-in |
✦ error[PermissionDenied]: unauthorized write operation to path '/var/log/app.log'
--> src/logger.bmd:22:5
|
21 | let entry = format_entry(level, msg);
22 | file_write("/var/log/app.log", entry);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [Hint: Run with the required security permission flag: --allow-run, --allow-write, --allow-net, or --allow-all]
23 | };
|
Resolution: Run the binary with the appropriate permission flag:
bramand run --allow-write src/logger.bmd or declare permissions.filesystem_write: true inside your project's bmdpkg.json manifest.
5. Concurrency & Deadlocks (ConcurrencyError)
BramandScript utilizes lightweight concurrent tasks (spawn), typed channels (chan(cap)), and mutual exclusion locks (mutex()). Thread-safety violations, sends to closed channels, or unbuffered deadlock stalls trigger concurrency diagnostics.
Case 5.1: Send on Closed Channel
Attempting to push data into a channel that has already been closed with ch.close() halts the emitting task.
let ch = chan(5);
ch.close();
// Guard against closed channel before sending
let ok = ch.try_send("payload");
if !ok {
println("Channel closed or buffer full; discarded gracefully.");
}
Case 5.2: Shared Memory Protection
BramandScript Hash maps and Arrays are internally guarded with sync.RWMutex to eliminate Go runtime race panics. For application-level atomic critical sections, use the stdlib mutex() primitive:
let m = mutex();
let sharedCounter = 0;
spawn(fn() {
m.lock();
sharedCounter += 1;
m.unlock();
});
6. Package Manager & Bundler (PackageManagerError)
The centralized package engine (bramand pkg, bramand install, bramand build) enforces SHA-256 supply-chain verification and offline determinism.
Occurs when the downloaded package archive's computed SHA-256 hash differs from the value recorded in bmdpkg.lock.
Fix: Run bramand cache prune to purge corrupt cached files, or re-verify dependencies with bramand pkg verify.
When running with --offline or BMD_OFFLINE=1, outbound network requests to package repositories are strictly prohibited.
Fix: Pre-cache dependencies using bramand get while online, or embed modules directly into single-binary executables using bramand build.
7. Stack Overflow & Cyclic Limits (StackOverflowError)
To protect systems from runaway recursion or cyclic data structure crashes, the Bramand VM implements hardware-safe depth limits.
Uncontrolled recursive functions without terminating base conditions are intercepted before exhausting host memory:
Deeply nested or circular structures (e.g. a[0] = a; a == a) use pointer identity shortcuts and depth tracking to prevent infinite recursion.
Canonical Language Truthiness & Falsy Evaluation Semantics
BramandScript enforces an unambiguous, standardized truthiness model across the Pratt parser, bytecode compiler, and VM runtime execution loop. The evaluation logic is synchronized between vm.isTruthy(), opcode jumps (OpJumpNotTruthy, OpJumpTruthy), short-circuit operators (&&, ||), logical negation (!), and builtin conversions (bool()).
| Type Category | Values | Truthiness | Evaluation Rule & VM Behavior |
|---|---|---|---|
BOOLEAN | false | Falsy | Evaluates directly to false. |
BOOLEAN | true | Truthy | Evaluates directly to true. |
NULL | null | Falsy | Absence of value is always falsy. |
INTEGER | 0 | Falsy | Integer zero is canonically falsy. |
INTEGER | 1, -1, 42 | Truthy | Any non-zero integer is truthy. |
FLOAT | 0.0, -0.0 | Falsy | Floating point zero is canonically falsy. |
FLOAT | 0.1, -3.14 | Truthy | Any non-zero floating point value is truthy. |
STRING | "" (empty string) | Falsy | Zero-length strings evaluate to false in all conditions. |
STRING | "0", "false", "text" | Truthy | Any string with length > 0 is truthy. |
BYTES | bytes(0), empty buffer | Falsy | Zero-length byte buffers evaluate to false. |
BYTES | bytes(1), bytes("a") | Truthy | Any byte buffer with length > 0 is truthy. |
COMPLEX / OBJECTS | [], {}, functions, structs | Truthy | References and compound data structures are always truthy. |
// 1. Logical OR Fallback (Preserves operands)
let user_name = "" || "Guest"; // "Guest" (empty string is falsy)
let timeout = 0 || 30; // 30 (0 is falsy)
let buffer = bytes(0) || bytes(128); // bytes(128)
// 2. Control Flow Branching (Direct falsy jumping)
if ("") {
println("Will not execute");
} else {
println("Empty string is cleanly falsy");
}
// 3. Logical NOT Inversion
println(!0); // true
println(!""); // true
println(!bytes(0)); // true
println(!null); // true
// 4. Canonical Boolean Coercion
println(bool("")); // false
println(bool(0)); // false
println(bool("Bramand")); // true
Defensive Engineering & Exception Recovery
BramandScript provides modern error handling constructs including structured exceptions (try-catch-finally), custom throw, LIFO defer statements, safe navigation, and resilient stdlib packages.
try {
let res = parse_user_input(payload);
if res.age < 0 {
throw "InvalidAge: age cannot be negative";
}
process_user(res);
} catch (err) {
println("✦ Caught Error: " + str(err));
} finally {
// Guarantees resource cleanup even after exceptions
cleanup_temp_buffers();
}
LIFO Defer Statement Cleanup
The defer keyword schedules cleanup actions to execute in strict Last-In, First-Out (LIFO) order when the enclosing function returns or unwinds via exception:
fn process_file(path) {
let fd = file_open(path, "r");
defer file_close(fd); // Guarantees closure upon return
let content = file_read(path);
return parse(content);
}
Safe Navigation & Nullish Operators
Prevent runtime null reference exceptions without cluttered nested conditional branches:
Returns null safely if user or address is null instead of throwing an error.
Evaluates right side only if the left operand is explicitly null.
Assigns fallback value only if target property is currently null.
Monadic Safety & The Try-Propagation Operator (?)
While try / catch is suited for unrecoverable infrastructure crashes, application domain logic in BramandScript is designed around first-class Algebraic Monads (Result and Option). Combining monadic containers with the postfix ? operator enables zero-overhead error propagation without viral try-catch wrapping.
1. Result Monad: Ok(v) and Err(e)
Functions that may fail return a Result container. Callers can unwrap safely, provide default fallbacks, or transform the inner value via functional chaining:
| Method | Signature | Behavior |
|---|---|---|
| is_ok | res.is_ok() | Returns true if result contains Ok value. |
| is_err | res.is_err() | Returns true if result contains Err error. |
| unwrap | res.unwrap() | Extracts inner value if Ok; raises error if called on Err. |
| unwrap_or | res.unwrap_or(default) | Extracts inner value if Ok, otherwise returns default without panicking. |
| unwrap_err | res.unwrap_err() | Extracts the error object from an Err container. |
| map | res.map(fn(v) { ... }) | Applies transform function to value if Ok; passes Err through untouched. |
| flat_map | res.flat_map(fn(v) { ... }) | Chains operations returning Result, flattening the nested monadic layer. |
2. Option Monad: Some(v) and None()
Nullable or optional values are represented by Option. Calling .is_some(), .is_none(), or .unwrap_or(default) avoids unexpected null pointer runtime bugs:
let find_user = fn(id) {
if id == 42 {
return Some({ "id": 42, "name": "Kiran" });
}
return None();
};
let user_opt = find_user(100);
println("User exists:", user_opt.is_some()); // false
let fallback_user = user_opt.unwrap_or({ "id": 0, "name": "Guest" });
println("Active user:", fallback_user["name"]); // "Guest"
3. The Postfix Try-Propagation Operator (?)
In BramandScript, the ? operator evaluates expressions containing Result or Option. If the value is Ok(v) or Some(v), it extracts v and execution proceeds sequentially. If the value is Err(e) or None(), the current frame unwinds immediately and returns the failure to the caller:
let read_port = fn(raw_str) {
if len(raw_str) == 0 {
return Err("empty port string");
}
let p = int(raw_str);
if p < 1 || p > 65535 {
return Err("port out of range [1, 65535]");
}
return Ok(p);
};
let load_server_config = fn(port_str) {
// If read_port fails, load_server_config immediately returns the Err!
let port = read_port(port_str)?;
return Ok({
"host": "0.0.0.0",
"port": port,
"ready": true
});
};
let valid_conf = load_server_config("8080");
println("Port configured:", valid_conf.unwrap()["port"]); // 8080
let invalid_conf = load_server_config("99999");
println("Error caught:", invalid_conf.unwrap_err()); // "port out of range [1, 65535]"
Resilience: Retry, Circuit Breaker & Structured Logging
For network and distributed systems, the standard library provides battle-tested resilience primitives in std/retry, std/circuitbreaker, and std/log:
import "std/retry" as retry;
import "std/circuitbreaker" as cb;
import "std/log" as log;
let breaker = cb.new_circuit_breaker(5, 10000); // 5 failures -> Open for 10s
let result = retry.with_retry(fn() {
return breaker.execute(fn() {
return fetch_remote_resource();
});
}, 3, 200); // 3 retries, 200ms initial exponential backoff
Quick Reference Troubleshooting Matrix
| Error Message Pattern | Classification | Root Cause | Recommended Fix |
|---|---|---|---|
| undefined variable 'x' | CompileError | Variable not in scope or typo | Declare with let x = ... or verify spelling |
| cannot reassign to constant | CompileError | Modifying immutable const |
Change declaration from const to let |
| division by zero | RuntimeError | Denominator evaluates to 0 | Add zero check: b == 0 ? 0 : a / b |
| index out of bounds | RuntimeError | Index outside [0, len-1] | Validate index with len(arr) before access |
| calling non-function: null | RuntimeError | Target variable is null | Verify function is assigned, or use optional call |
| unauthorized write operation | PermissionDenied | Security sandbox blocked disk write | Execute with --allow-write or --allow-all |
| stack overflow: 10000 frames | RuntimeError | Infinite or unbounded recursion | Add terminating base case or rewrite loop iteratively |
| circular import detected | CompileError | Module A imports B which imports A | Refactor common dependencies into a shared module |
| comptime compilation error | ComptimeError | Syntax or semantic defect inside comptime { ... } block | Ensure comptime code is self-contained and valid BramandScript syntax |
| comptime evaluation error | ComptimeError | Runtime exception encountered inside compile-time VM sandbox | Fix runtime errors (e.g. index bounds or nil dereference) within comptime expression |
| SQL parse error: ... | SQLError | Malformed SQL query, missing FROM, or invalid clause | Verify ANSI SQL syntax: SELECT cols FROM table WHERE cond |
| SQL execution error: table not found | SQLError | Referenced table missing from tables hash dictionary | Pass table key in dataset dictionary: { "users": records } |
| vector dimensions mismatch | AIError | Vectors passed to dot_product, cosine, or euclidean have unequal lengths | Ensure query vector and indexed corpus vectors share identical embedding dimensions |