garrytan/gstack · error · Error

Invalid heatmap JSON. Expected object: '{"@e1":"green","@e3"

Error message

Invalid heatmap JSON. Expected object: '{"@e1":"green","@e3":"red"}'

What it means

Thrown when opts.heatmap cannot be parsed into a plain JSON object. The value is run through JSON.parse and then type-checked: anything that is null, an array, a primitive, or syntactically invalid JSON all funnel into this single message. The expected shape is an object whose keys are refs and values are color names.

Source

Thrown at browse/src/snapshot.ts:495

    const VALID_COLORS = new Set(['green', 'yellow', 'red', 'blue', 'orange', 'gray']);
    const COLOR_MAP: Record<string, { border: string; bg: string }> = {
      green:  { border: '#00b400', bg: 'rgba(0,180,0,0.15)' },
      yellow: { border: '#ffb400', bg: 'rgba(255,180,0,0.15)' },
      red:    { border: '#ff0000', bg: 'rgba(255,0,0,0.15)' },
      blue:   { border: '#0066ff', bg: 'rgba(0,102,255,0.15)' },
      orange: { border: '#ff6600', bg: 'rgba(255,102,0,0.15)' },
      gray:   { border: '#888888', bg: 'rgba(136,136,136,0.15)' },
    };

    let colorAssignments: Record<string, string>;
    try {
      const parsed = JSON.parse(opts.heatmap);
      if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
        throw new Error('not an object');
      }
      colorAssignments = parsed;
    } catch {
      throw new Error('Invalid heatmap JSON. Expected object: \'{"@e1":"green","@e3":"red"}\'');
    }

    // Validate colors
    for (const [ref, color] of Object.entries(colorAssignments)) {
      if (!VALID_COLORS.has(color)) {
        throw new Error(`Invalid heatmap color "${color}" for ${ref}. Valid: ${[...VALID_COLORS].join(', ')}`);
      }
    }

    try {
      const boxes: Array<{ ref: string; box: { x: number; y: number; width: number; height: number }; color: string }> = [];
      for (const [refKey, color] of Object.entries(colorAssignments)) {
        const cleanRef = refKey.startsWith('@') ? refKey.slice(1) : refKey;
        const entry = refMap.get(cleanRef);
        if (!entry) continue; // Skip refs not found on page
        try {
          const box = await entry.locator.boundingBox({ timeout: 1000 });
          if (box) {

View on GitHub (pinned to 94993f7401)

Solutions

  1. Quote the entire JSON object in the shell and use double quotes inside: `-H '{"@e1":"red","@e3":"green"}'`.
  2. Validate the string with `jq .` or `JSON.parse` in a REPL before passing it.
  3. Ensure the value parses to a plain object — not an array, not a string, not null.
  4. If building the option programmatically, use `JSON.stringify(obj)` rather than hand-concatenating.

Example fix

// before
snapshot(page, { heatmap: '@e1:red' }); // not JSON
// after
snapshot(page, { heatmap: JSON.stringify({ '@e1': 'red' }) });
Defensive patterns

Strategy: validation

Validate before calling

function asHeatmapObject(s: string): Record<string, string> {
  let parsed: unknown;
  try { parsed = JSON.parse(s); } catch { throw new Error('Invalid heatmap JSON. Expected object: {"@e1":"green"}'); }
  if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
    throw new Error('Invalid heatmap JSON. Expected object: {"@e1":"green"}');
  }
  return parsed as Record<string, string>;
}

Type guard

function isPlainObject(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

Try / catch

try {
  await snapshot(session, { heatmap: userInput });
} catch (e: any) {
  if (/^Invalid heatmap JSON/.test(e.message)) {
    // surface to the user with the expected shape
    throw new Error('heatmap must be a JSON object like {"@e1":"red"}');
  } else throw e;
}

Prevention

When it happens

Trigger: Passing -H/--heatmap a string that is not valid JSON (e.g. `@e1=red`), valid JSON of the wrong type (e.g. `["@e1","red"]` or `'"green"'`), or `null`.

Common situations: Shell quoting that strips the braces (`-H {@e1:red}`); passing the heatmap as a path to a JSON file instead of the contents; typos like missing quotes around keys; copy-pasting YAML by mistake.

Related errors


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/b66fbe55f3e0d573. Report an issue: GitHub.