---
title: "Error reports: text, compact and JSON"
url: https://recursiveai.org/oneecma/cookbook/error-reports/
description: "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…"
version: "1.0.0"
---

# Error reports: text, compact and JSON

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`.

```csharp
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}");
}
```

```text
--- 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.message` and `err.stack` inside 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 across `await`.
- **`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 across `await` are marked async, and .NET frames of host functions sit between script frames.
- **`locals in`** lines (with `ShowLocals`) 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](https://recursiveai.org/oneecma/cookbook/record-replay/)). 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; `Indented` is shown here for reading.
- **Objects**: `Diagnostic` has `Kind`, `Code`, `Headline`, `PrimarySpan`, `SecondaryLabels`, `Help`, `Notes`, `Suggestions`, `Provenance` and `Stack`, 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](https://github.com/recursive-ai-oss/oneecma-csharp/blob/v1.0.0/docs/diagnostic-report.schema.json) (JSON Schema 2020-12), and every field is described in [the JSON report](https://recursiveai.org/oneecma/docs/diagnostic-json/).

## Good to know

- Syntax errors come in the same forms, with every error of one parse in one report: `DiagnosticRenderer.RenderAll` and `DiagnosticJson.Render` take a list.
- `oneecma run --compact`, `--locals` and `oneecma check --format json` print the same forms from the command line: see [The oneecma command](https://recursiveai.org/oneecma/cookbook/command-line/).
- How the `help:` line is chosen is in [How help lines are chosen](https://recursiveai.org/oneecma/docs/error-help/); the `err.stack` format is in [stack traces](https://recursiveai.org/oneecma/docs/stack-traces/).
