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
- Quote the entire JSON object in the shell and use double quotes inside: `-H '{"@e1":"red","@e3":"green"}'`.
- Validate the string with `jq .` or `JSON.parse` in a REPL before passing it.
- Ensure the value parses to a plain object — not an array, not a string, not null.
- 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
- Always build the heatmap with JSON.stringify rather than string concatenation.
- Validate with JSON.parse in a REPL or `jq .` before passing the CLI flag.
- Shell-quote the whole JSON object with single quotes outside, double inside.
- Reject array/primitive inputs at your own argument parser before reaching snapshot.
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
- Invalid heatmap color "${color}" for ${ref}. Valid: ${[...VA
- pdf: --from-file requires a path
- Unknown pdf flag: ${a}
- pdf: --from-file ${payloadPath} is not valid JSON (${msg}).
- pdf: --from-file ${payloadPath} must be a JSON object, got $
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/b66fbe55f3e0d573.
Report an issue: GitHub.