How GocciaScript reports errors to script authors, CLI users, and embedders.
- Error types --
Error,TypeError,ReferenceError,RangeError,SyntaxError,URIError,AggregateError,SuppressedError, plusTimeoutErrorfor the--timeoutflag - Parser errors -- Displayed with source context, a caret pointing to the exact column, and optional suggestion text (e.g., "Use 'let' or 'const' instead")
- Runtime errors -- Carry
name,message,stack, and optionalcause; catchable withtry/catch/finally - JSON output --
--output=jsonwraps every execution result in a structured envelope withok,error.type,error.message,error.line, anderror.column.--output=compact-jsonproduces the same envelope without thebuild,memory,stdout, orstderrfields, leaving only the normalizedoutputarray and structurederrorfor console output Error.cause-- All error constructors accept an options bag with acauseproperty for error chaining (ES2022+)
GocciaScript supports the standard ECMAScript error constructors plus two additional types. All JavaScript-visible error types inherit from Error and work with instanceof. TimeoutError is CLI-only and not exposed as a JavaScript constructor.
| Type | Thrown when | MDN |
|---|---|---|
Error |
Generic errors; base class for all error types | Error |
TypeError |
Property access on null/undefined, calling a non-function, reassigning const, calling a constructor without new |
TypeError |
ReferenceError |
Accessing an undeclared variable, using a variable before initialization (TDZ) | ReferenceError |
RangeError |
Invalid array length, negative ArrayBuffer size, out-of-range numeric conversions, call stack depth exceeded (--stack-size) |
RangeError |
SyntaxError |
Invalid syntax detected by the parser or lexer; also throwable at runtime via new SyntaxError(...) |
SyntaxError |
URIError |
Malformed URI passed to encodeURI, decodeURI, encodeURIComponent, or decodeURIComponent |
URIError |
AggregateError |
Multiple errors wrapped together; used by Promise.any when all promises reject |
AggregateError |
SuppressedError |
Disposal error during explicit resource management (using/await using); wraps both the new and suppressed error |
SuppressedError |
TimeoutError |
Execution exceeded the --timeout limit (CLI only; not a JS-visible constructor) |
-- |
All error types follow the standard prototype chain:
const e = new TypeError("bad type");
e instanceof TypeError; // true
e instanceof Error; // true
e instanceof RangeError; // falseErrors thrown internally by the engine (e.g., property access on null) use the same prototype chain as user-constructed errors, so instanceof checks work consistently in catch blocks.
GocciaScript implements the ES2026 Error.isError static method, which checks for the internal [[ErrorData]] slot:
Error.isError(new TypeError("x")); // true
Error.isError({ message: "fake" }); // false — plain objects are not errorsWhen the parser encounters invalid syntax, it displays a detailed error with source context. The format includes the error name, message, file location, the offending source line, and a caret (^) pointing to the exact column:
SyntaxError: Expected ';' after expression
--> script.js:3:12
1 | const x = 1
2 | const y = 2
3 | const z = 3 + +
^
4 | console.log(z);
Up to 2 lines of context are shown before and after the error line.
Some parser errors include a suggestion line that recommends an alternative. GocciaScript intentionally excludes certain JavaScript features and uses suggestions to guide users toward the supported alternatives:
SyntaxError: 'var' declarations are not supported in GocciaScript
Suggestion: Use 'let' or 'const' instead
--> script.js:1:1
1 | var x = 42;
^
Other features that produce suggestions include:
vardeclarations -- suggestsletorconstfunctiondeclarations and expressions -- suggests arrow functions==/!=(loose equality) -- suggests===/!==- Traditional loops (
for,while,do...while) -- suggestsfor...ofor array methods withstatements -- no alternative (excluded for security)- Default imports/exports -- suggests named imports/exports
See Language for the full list of excluded features and their rationale.
When an uncaught runtime error reaches the top level, GocciaScript displays the error with the same source-context format used by parser errors. The stack trace from the error's stack property is used to locate the offending line in source:
TypeError: Cannot read properties of undefined (reading 'x')
--> script.js:5:10
3 | const getX = (obj) => {
4 | return obj.x;
5 | return obj.nested.x;
^
6 | };
7 | getX(undefined);
Stdin input uses <stdin> as the filename and retains full source context for error display. If source context is not available (e.g., errors originating inside native built-in callbacks without a JavaScript source location), GocciaScript falls back to displaying the stack trace string.
Every error object has the following properties:
| Property | Type | Description |
|---|---|---|
name |
string |
Error type name (e.g., "TypeError", "RangeError") |
message |
string |
Human-readable error description |
stack |
string |
Formatted stack trace (see Stack Traces) |
cause |
any | Optional; present only when constructed with { cause } option (see Error.cause) |
AggregateError adds:
| Property | Type | Description |
|---|---|---|
errors |
Array |
The array of errors passed to the constructor |
SuppressedError adds:
| Property | Type | Description |
|---|---|---|
error |
any | The error that triggered the suppression |
suppressed |
any | The original error that was suppressed |
The stack property contains a V8-style formatted string with the error header followed by at frames:
TypeError: Cannot read properties of null
at inner (script.js:2:10)
at middle (script.js:5:3)
at outer (script.js:8:3)
Each frame shows the function name (or <anonymous>), the file path, and the line and column number. Frames are listed from innermost (most recent) to outermost.
Most error constructors accept an options object with a cause property, following ES2022 Error Cause. The options argument position varies by constructor: second for Error, TypeError, RangeError, ReferenceError, SyntaxError, and URIError; third for AggregateError (new AggregateError(errors, message, options)); fourth for SuppressedError (new SuppressedError(error, suppressed, message, options)).
const original = new Error("disk full");
const wrapped = new Error("save failed", { cause: original });
wrapped.message; // "save failed"
wrapped.cause.message; // "disk full"Error cause chaining works across error types:
const root = new RangeError("out of bounds");
const mid = new TypeError("invalid type", { cause: root });
const top = new Error("operation failed", { cause: mid });
top.cause.cause.message; // "out of bounds"The cause property:
- Can be any value (string, number, object, another error,
null,undefined) - Is writable and configurable but not enumerable
- Is only present when the options object has a
causeproperty (not present by default) - Works with all error types:
Error,TypeError,RangeError,ReferenceError,SyntaxError,URIError,AggregateError, andSuppressedError
Error handling follows standard ECMAScript semantics. See Language for the full syntax reference.
try {
const x = null;
x.property; // throws TypeError
} catch (e) {
console.log(e.name); // "TypeError"
console.log(e.message); // "Cannot read properties of null (reading 'property')"
}The catch parameter can be omitted (ES2019+):
try {
riskyOperation();
} catch {
console.log("something went wrong");
}The finally block always runs, whether or not an error was thrown:
try {
return computeResult();
} finally {
cleanup(); // runs even when try returns
}Per the spec, if finally contains a return, throw, or break, it overrides the try/catch result.
GocciaScript allows throwing any value, not just error objects:
try {
throw "string error";
} catch (e) {
console.log(e); // "string error"
}
try {
throw 42;
} catch (e) {
console.log(e); // 42
}Use instanceof to differentiate error types in a catch block:
try {
someOperation();
} catch (e) {
if (e instanceof TypeError) {
console.log("type error:", e.message);
} else if (e instanceof RangeError) {
console.log("range error:", e.message);
} else {
throw e; // rethrow unknown errors
}
}When using using or await using declarations for explicit resource management, if both the block body and a resource's [Symbol.dispose]() method throw, the runtime wraps both errors in a SuppressedError:
let caught;
try {
using resource = {
[Symbol.dispose]() { throw new Error("disposal failed"); }
};
throw new Error("block failed");
} catch (e) {
caught = e;
}
caught instanceof SuppressedError; // true
caught.error.message; // "disposal failed"
caught.suppressed.message; // "block failed"If multiple disposals fail, errors are chained: each new disposal error wraps the previous SuppressedError as its suppressed property.
SuppressedError can also be constructed directly:
const err = new SuppressedError(
new Error("new"), // error
new Error("old"), // suppressed
"An error was suppressed" // message (optional)
);
err.name; // "SuppressedError"
err.error; // Error: new
err.suppressed; // Error: old
err.message; // "An error was suppressed"When running with --output=json, GocciaScript wraps every execution result in a structured JSON envelope. This is useful for programmatic consumers and embedding scenarios.
{
"ok": true,
"build": {
"version": "0.1.0-dev",
"date": "2026-04-27",
"commit": "abc1234",
"os": "darwin",
"arch": "aarch64"
},
"stdout": "hello\n",
"stderr": "",
"output": ["hello"],
"error": null,
"timing": {
"lex_ns": 500000,
"parse_ns": 1200000,
"compile_ns": 0,
"exec_ns": 3100000,
"total_ns": 4800000
},
"memory": {
"gc": {
"liveBytes": 2048,
"startLiveBytes": 0,
"endLiveBytes": 2048,
"peakLiveBytes": 4096,
"deltaLiveBytes": 2048,
"allocatedDuringRunBytes": 4096,
"maxBytes": 536870912,
"startObjectCount": 0,
"endObjectCount": 24,
"collections": 0,
"collectedObjects": 0
},
"heap": {
"startAllocatedBytes": 16384,
"endAllocatedBytes": 32768,
"deltaAllocatedBytes": 16384,
"startFreeBytes": 8192,
"endFreeBytes": 4096,
"deltaFreeBytes": -4096
}
},
"workers": { "used": 1, "available": 1, "parallel": false },
"files": [
{
"fileName": "script.js",
"ok": true,
"stdout": "hello\n",
"stderr": "",
"output": ["hello"],
"error": null,
"timing": {
"lex_ns": 500000,
"parse_ns": 1200000,
"compile_ns": 0,
"exec_ns": 3100000,
"total_ns": 4800000
},
"memory": { "gc": { "liveBytes": 2048 }, "heap": { "endAllocatedBytes": 32768 } },
"result": 42
}
]
}{
"ok": false,
"build": {
"version": "0.1.0-dev",
"date": "2026-04-27",
"commit": "abc1234",
"os": "darwin",
"arch": "aarch64"
},
"stdout": "",
"stderr": "",
"output": [],
"error": {
"type": "TypeError",
"message": "Cannot read properties of null (reading 'x')",
"line": 5,
"column": 10,
"fileName": "script.js"
},
"timing": {
"lex_ns": 500000,
"parse_ns": 1200000,
"compile_ns": 0,
"exec_ns": 100000,
"total_ns": 1800000
},
"memory": { "gc": { "liveBytes": 2048 }, "heap": { "endAllocatedBytes": 32768 } },
"workers": { "used": 1, "available": 1, "parallel": false },
"files": [
{
"fileName": "script.js",
"ok": false,
"stdout": "",
"stderr": "",
"output": [],
"error": {
"type": "TypeError",
"message": "Cannot read properties of null (reading 'x')",
"line": 5,
"column": 10,
"fileName": "script.js"
},
"timing": {
"lex_ns": 500000,
"parse_ns": 1200000,
"compile_ns": 0,
"exec_ns": 100000,
"total_ns": 1800000
},
"memory": { "gc": { "liveBytes": 2048 }, "heap": { "endAllocatedBytes": 32768 } },
"result": null
}
]
}| Field | Type | Description |
|---|---|---|
ok |
boolean |
true for success, false for error |
build |
object |
Build identity, including version, date, commit, os, and arch |
stdout |
string |
Unformatted stdout-oriented console output; present even when empty |
stderr |
string |
Unformatted stderr-oriented console output; present even when empty |
output |
string[] |
Formatted console output split into lines |
error |
object | null |
First failed file's error details, or null when the run succeeds |
error.type |
string |
Error type name ("TypeError", "SyntaxError", "TimeoutError", etc.) |
error.message |
string |
Error message text |
error.line |
number | null |
Source line number (1-based), or null if unavailable |
error.column |
number | null |
Source column number (1-based), or null if unavailable |
error.fileName |
string | null |
Source file path, or null if unavailable |
timing |
object |
Cumulative phase-level timings in nanoseconds (*_ns) |
memory |
object | null |
GC and application heap measurements for the run |
memory.gc.liveBytes |
number |
GC-managed bytes live at the measurement endpoint. This is the report equivalent of Goccia.gc.bytesAllocated |
memory.gc.allocatedDuringRunBytes |
number |
Total GC-managed bytes allocated during the measured run, including allocations later collected |
memory.gc.peakLiveBytes |
number |
Highest live GC-managed byte count observed during the measurement |
workers |
object |
Worker logistics: used worker count, available worker count, and whether the run was parallel |
files |
object[] |
Per-input results. Single-file runs use the same structure with one element |
files[].fileName |
string |
Input file path or <stdin> |
files[].result |
any | The script completion value for that input. Serializes as null for both errors and JavaScript undefined; use files[].ok and files[].error to distinguish those cases. |
--output=compact-json emits the same envelope as --output=json with the build, memory, stdout, and stderr fields omitted at both the top level and per-file. All console output is still available through the normalized output array (lines from console.log/info/debug and prefixed lines like Error: ... or Warning: ... from console.error/warn); script errors remain available through the structured error object. Use this format when you do not need build identity, memory measurements, or the raw stdout/stderr split — and want a smaller payload.
{
"ok": true,
"output": ["hello", "Error: oops"],
"error": null,
"timing": {
"lex_ns": 500000,
"parse_ns": 1200000,
"compile_ns": 0,
"exec_ns": 3100000,
"total_ns": 4800000
},
"workers": { "used": 1, "available": 1, "parallel": false },
"files": [
{
"fileName": "script.js",
"ok": true,
"output": ["hello", "Error: oops"],
"error": null,
"timing": {
"lex_ns": 500000,
"parse_ns": 1200000,
"compile_ns": 0,
"exec_ns": 3100000,
"total_ns": 4800000
},
"result": 42
}
]
}When execution exceeds the --timeout limit, the JSON envelope reports a TimeoutError:
{
"ok": false,
"build": { "version": "0.1.0-dev", "date": "2026-04-27", "commit": "abc1234", "os": "darwin", "arch": "aarch64" },
"stdout": "",
"stderr": "",
"output": [],
"error": {
"type": "TimeoutError",
"message": "Execution timed out after 100ms",
"line": null,
"column": null,
"fileName": null
},
"timing": { "lex_ns": 100000, "parse_ns": 200000, "compile_ns": 0, "exec_ns": 100000000, "total_ns": 100300000 },
"memory": {
"gc": {
"liveBytes": 8192,
"startLiveBytes": 0,
"endLiveBytes": 8192,
"peakLiveBytes": 16384,
"deltaLiveBytes": 8192,
"allocatedDuringRunBytes": 16384,
"maxBytes": 536870912,
"startObjectCount": 0,
"endObjectCount": 80,
"collections": 0,
"collectedObjects": 0
},
"heap": {
"startAllocatedBytes": 16384,
"endAllocatedBytes": 32768,
"deltaAllocatedBytes": 16384,
"startFreeBytes": 8192,
"endFreeBytes": 4096,
"deltaFreeBytes": -4096
}
},
"workers": { "used": 1, "available": 1, "parallel": false },
"files": [
{
"fileName": "<stdin>",
"ok": false,
"stdout": "",
"stderr": "",
"output": [],
"error": {
"type": "TimeoutError",
"message": "Execution timed out after 100ms",
"line": null,
"column": null,
"fileName": null
},
"timing": { "lex_ns": 100000, "parse_ns": 200000, "compile_ns": 0, "exec_ns": 100000000, "total_ns": 100300000 },
"memory": { "gc": { "liveBytes": 8192 }, "heap": { "endAllocatedBytes": 32768 } },
"result": null
}
]
}For Pascal-side error handling when embedding GocciaScript in FreePascal applications, see Embedding the Engine. The engine raises TGocciaError subclasses (TGocciaSyntaxError, TGocciaTypeError, TGocciaReferenceError) on the Pascal side and TGocciaThrowValue for JS-level throw statements.
- Language -- Supported features, excluded features, and rationale
- Built-in Objects -- Error constructors and API reference
- Embedding the Engine -- Pascal-side error handling for embedders
- Testing -- Writing tests that assert on error behavior