Get OneEcma’s report for a failed script in each of its forms: the full text report, the same with each frame’s local variables, a compact rendering sized for an LLM’s context, and versioned JSON for tools. All of them come from one Diagnostic.
The script reads order.items where the object holds lineItems.
using RecursiveAI.OneEcma; using RecursiveAI.OneEcma.Diagnostics; const string checkout = """ async function loadOrder(id) { return { id, lineItems: [{ price: 4 }, { price: 6 }], total: 10 }; } async function main() { const order = await loadOrder(7); const total = order.items.reduce((sum, i) => sum + i.price, 0); return total; } main(); """; var engine = new ScriptEngine(); try { engine.Evaluate(checkout, "checkout.js"); // the rejection is unhandled, so Evaluate throws } catch (JsException e) { Diagnostic report = e.Report!; Console.WriteLine("--- e.Message"); Console.WriteLine(e.Message); Console.WriteLine("--- the full report"); Console.WriteLine(DiagnosticRenderer.Render(report)); Console.WriteLine("--- with locals"); Console.WriteLine(DiagnosticRenderer.Render(report, new RenderOptions { ShowLocals = true })); Console.WriteLine("--- compact, for a model's context"); Console.WriteLine(DiagnosticRenderer.Render(report, new RenderOptions { Compact = true })); Console.WriteLine("--- JSON"); Console.WriteLine(DiagnosticJson.Render([report], new JsonRenderOptions { Indented = true })); Console.WriteLine("--- the same facts as objects"); Console.WriteLine($"{report.Kind} {report.Code} at {report.Position.Line}:{report.Position.Column}"); foreach (DidYouMean d in report.Suggestions) Console.WriteLine($"did you mean {d.Name}: {d.Description}"); }
--- e.Message TypeError: Cannot read properties of undefined (reading 'reduce') --- the full report error[TypeError]: Cannot read properties of undefined (reading 'reduce') --> checkout.js:7:23 | 6 | const order = await loadOrder(7); | ------------------ `order` comes from here: { id, lineItems, total } 7 | const total = order.items.reduce((sum, i) => sum + i.price, 0); | ^^^^^ `order.items` is undefined | = help: did you mean `order.lineItems`? (Array, 2 items) = stack: main (checkout.js:7) --- with locals error[TypeError]: Cannot read properties of undefined (reading 'reduce') --> checkout.js:7:23 | 6 | const order = await loadOrder(7); | ------------------ `order` comes from here: { id, lineItems, total } 7 | const total = order.items.reduce((sum, i) => sum + i.price, 0); | ^^^^^ `order.items` is undefined | = help: did you mean `order.lineItems`? (Array, 2 items) = stack: main (checkout.js:7) = locals in main: order = { id, lineItems, total }, total = <uninitialized> --- compact, for a model's context error[TypeError]: Cannot read properties of undefined (reading 'reduce') --> checkout.js:7:23 6 | const order = await loadOrder(7); | ------------------ `order` comes from here: { id, lineItems, total } 7 | const total = order.items.reduce((sum, i) => sum + i.price, 0); | ^^^^^ `order.items` is undefined = help: did you mean `order.lineItems`? (Array, 2 items) --- JSON { "version": 1, "format": "oneecma.report", "summary": { "errors": 1, "warnings": 0, "repairs": 0 }, "diagnostics": [ { "kind": "TypeError", "code": "runtime", "severity": "error", "headline": "Cannot read properties of undefined (reading 'reduce')", "source": { "name": "checkout.js", "length": 255 }, "span": { "start": 185, "end": 190, "line": 7, "column": 23, "endLine": 7, "endColumn": 28 }, "label": "`order.items` is undefined", "secondary": [ { "span": { "start": 143, "end": 161, "line": 6, "column": 17, "endLine": 6, "endColumn": 35 }, "message": "`order` comes from here: { id, lineItems, total }" } ], "notes": [], "help": [ "did you mean `order.lineItems`? (Array, 2 items)" ], "fixes": [], "recovery": [], "stack": [ { "function": "main", "source": { "name": "checkout.js", "length": 255 }, "span": { "start": 179, "end": 190, "line": 7, "column": 17, "endLine": 7, "endColumn": 28 }, "async": false, "locals": [ { "name": "order", "value": "{ id, lineItems, total }" }, { "name": "total", "value": "<uninitialized>" } ] } ], "provenance": [ { "source": { "name": "checkout.js", "length": 255 }, "span": { "start": 143, "end": 161, "line": 6, "column": 17, "endLine": 6, "endColumn": 35 }, "description": "`order` comes from here: { id, lineItems, total }" } ], "didYouMean": [ { "name": "order.lineItems", "description": "Array, 2 items" } ] } ] } --- the same facts as objects TypeError runtime at 7:23 did you mean order.lineItems: Array, 2 items
Reading the report
- The headline is V8’s wording, word for word, and
err.messageanderr.stackinside the script are what V8 prints too. Code that parses them keeps working. - The primary underline marks the failing expression and says what was wrong with it.
- Secondary labels (
-underlines) show where the failing value came from, including acrossawait. help:suggests a fix. A “did you mean” candidate is always something the object or scope actually holds, never a guess.stack:lists the frames, innermost first. Frames stitched acrossawaitare marked async, and .NET frames of host functions sit between script frames.locals inlines (withShowLocals) preview each frame’s bindings, at most 24 per frame and 60 characters per value.
The explanation comes from replaying the failed run with tracing on: the normal run only records what it took from outside (see Record a failed run and replay it). A run that succeeds pays nothing for it.
Which form to use
- Full text (
DiagnosticRenderer.Render) for logs and people. - Compact (
RenderOptions.Compact) for a model’s context: headline, location, the source line with its underline and label, the origin label, the single most useful help, and a one-line stack only when there is more than one frame. Long lines are cut to about 100 columns. - JSON (
DiagnosticJson.Render) for tools. It is compact on one line by default;Indentedis shown here for reading. - Objects:
DiagnostichasKind,Code,Headline,PrimarySpan,SecondaryLabels,Help,Notes,Suggestions,ProvenanceandStack, if you’d rather build your own message.
The JSON contract
The format is versioned: "version": 1, and within a version fields are only added, so a consumer ignores properties it doesn’t know. Offsets are UTF-16 code units, half-open; lines and columns are 1-based, with columns in UTF-16 code units as in err.stack. Core arrays (secondary, notes, help, fixes, recovery) are always present; the runtime members (stack, provenance, didYouMean) appear only when they have something to say. The same bytes come out for the same diagnostics.
The schema is diagnostic-report.schema.json (JSON Schema 2020-12), and every field is described in the JSON report.
Good to know
- Syntax errors come in the same forms, with every error of one parse in one report:
DiagnosticRenderer.RenderAllandDiagnosticJson.Rendertake a list. oneecma run --compact,--localsandoneecma check --format jsonprint the same forms from the command line: see The oneecma command.- How the
help:line is chosen is in How help lines are chosen; theerr.stackformat is in stack traces.