Recipes

Debug Broken JSON

Your mock responds, but the JSON comes back as unformatted raw text, or the client refuses to parse it. This page is a checklist of the four template bugs that cause almost every broken JSON body, in the order you should suspect them.

How Mocko tells you

When a mock declares a JSON content type, Mocko validates the rendered body. Valid JSON is pretty-printed automatically; invalid JSON is returned as-is, and the server logs an error saying the body was not valid JSON. So an unformatted response is the signal: the template rendered something broken.

Suspect 1: a trailing comma from each

The classic. isLast only exists inside forEach. Inside each it is undefined, so the negated block {{^isLast}},{{/isLast}} renders its comma on every iteration, including the last:

{{! BROKEN: renders [1,2,3,] }}
[
{{#each data.catalog.product}}
{{$this.id}}{{^isLast}},{{/isLast}}
{{/each}}
]

{{! FIXED: forEach provides a real isLast }}
[
{{#forEach data.catalog.product}}
{{item.id}}{{^isLast}},{{/isLast}}
{{/forEach}}
]
Rule of thumb: any loop that produces comma-separated output, which means almost any JSON array, should use forEach.

Suspect 2: an empty value from a context change

A field renders as nothing, leaving invalid JSON like "id": ,. The usual cause: reading request or data inside a block that changed the context, such as forEach, each, with, or a direct object block:

{{! BROKEN: request is out of scope inside the loop }}
{{#forEach data.catalog.product}}
{ "id": {{item.id}}, "requestedBy": "{{request.headers.x-user}}" }{{^isLast}},{{/isLast}}
{{/forEach}}

{{! FIXED: extract to a variable first }}
{{= $user request.headers.x-user}}
{{#forEach data.catalog.product}}
{ "id": {{item.id}}, "requestedBy": "{{$user}}" }{{^isLast}},{{/isLast}}
{{/forEach}}

$root.request... also works when a variable feels like overkill. The mechanics are explained on the Templating page. A related empty-value case: reading a named data sub-block as if it were an object. Sub-blocks are always arrays, see Data Blocks.

Suspect 3: missing quotes around a string

Template expressions render bare values; JSON string syntax is your job. "name": {{item.name}} renders "name": Widget, which is not JSON. Keep the quotes in the template: "name": "{{item.name}}". Numbers and booleans go unquoted; strings do not.

Suspect 4: literal braces

A body containing braces that are not template syntax, common when a payload embeds nested closing braces or example templates, fails to parse or renders oddly. Escape them as \{{ and \}}. In HCL quoted strings (not heredocs) double the backslash: \\{{.

Tools for narrowing it down

  • {{log 'here'}} prints to the server console, confirming which branch ran. Dump values with {{log (json request.body)}}.
  • Templates that fail to compile are reported when the mock loads, with a line and column pointer in the terminal, and on the mock itself in the UI.
  • Bisect big templates: comment out half the body with {{! ... }} and see if the JSON becomes valid.